当前位置:首页 » 国际新闻 » 正文

分类页和文章页“当前位置”下方广告(PC版)
分类页和文章页“当前位置”下方广告(移动版)

郭峰,Redis数据筛选算法,旅游

440 人参与  2019年07月16日 15:29  分类:国际新闻  评论:0  
  移步手机端

1、打开你手机的二维码扫描APP
2、扫描左则的二维码
3、点击扫描获得的网址
4、可以在手机端阅读此文章

众所周知,Redis的一切数据都存储在内存中,可是内存是一种有限的资源,所以为了避免Redis无约束的运用内存,在发动Redis时能够经过装备项 maxmemory 来指定其最大能运用的内存容量。例如能够经过以下装备来设置Redis最大能运用 1G 内存:

maxmemory 1G

当Redis运用的内存超越装备的 maxmemory 时,便会触发数据挑选战略。Redis供给了多种数据挑选的战略,如下:

  • volatile-lru: 最近最少运用算法,从设置了过期时刻的键中挑选空转时刻最长的键值对铲除去
  • volat冰点复原暗码ile-lfu: 最近最不常常运用算法,从设置了过期时刻的键中挑选某段时刻之内运用频次最小的键值对铲除去
  • volatile-ttl: 从设置了过期时刻的键世界上最帅的人中挑选过期时刻最早的键值对铲除
  • volatile-random: 从设置了过期时刻的键中,随机挑选键进行铲除
  • allkeys-lru: 最近最少运用算法,从一切的键中挑选空转时刻最长的键值对铲除
  • allkeys-lfu: 最近最不常常运用算法,从一切的键中挑选某段时刻之内运用频次最少的键值对铲除
  • allkeys-random: 一切的键中,随机挑选键进行删去
  • noeviction: 不做任何的整理作业,在redis的内存超越约束之后,一切的写入操作都会回来过错;可是读操作都能正常的进行

能够在发动Redis时,经过装备项 maxmemory_policy 来指定要运用的数据挑选战略。例如要运用 volatile-lru 战略能够经过以下装备来指定:

maxmemory_policy volatile-lru

LRU算法

LRU是 Least Recently Used 的缩写,即最近最少运用,许多缓存体系都运用此算法作为郭峰,Redis数据挑选算法,旅行挑选战略。

最简略的完成方法就是把所infinite有缓存经过一个链表连接起来,新创建的缓存添加到链表的头部,假如有缓存被拜访了,就把缓存移动到链表的头部。因为被拜访的缓存会移动到链表的头部,所以没有被拜访的缓存会跟着时刻的推移移动的链表的尾部,挑选数据时只需求从链表的尾部开端即可。下图展现了这个进程:

Redi郭峰,Redis数据挑选算法,旅行s的LRU算法

Redis运用了结构体 robj 来存储缓存目标,而 robj 结构有个名为 lru 的字段,用于记载缓存目标终究被拜访的时刻,Redis就是以 l深入敌后的奔跑ru 字段的值作为挑选依据。robj 结构如下:

typedef struct redisObject {
...
unsigned lru:24;
...
} robj;

当缓存目标被拜访时,便会更新此字段的值。代码如下:

robj *lookupKey(redisDb *db, robj *key, int flags) {
dictEntry *de = dictFind(db->dict,key->ptr);
if (de) {
robj *val = dictGetVal(de);
/* Update the access time for the ageing algorithm.
* Don't do it if we have a saving child, as this will trigger
* a copy on write madness. */
if (server.rdb_child_pid == -1 &&
server.aof_child_pid == -1 &&
!(flags & LOOKUP_NOTOUCH))
{
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
updateLFU(val);
} else {
val->lru = LRU_CLOCK(); // 更新lru字段的值
}
}
return val;
} else {
return NULL;
}
}

lookupKey() 函数用于查找key对应的缓存目标,所以当缓存目标被拜访时便会调用此函数。

Redis数据挑选

接下来咱们剖析一下当Redis内存运用超越装备的最大内存运用约束时的处理方法。

Redis在处理每一个指令时都会查看内存的运用是否超越了约束的最大值,处理指令是经过 processCommand() 函数进行的,查看内存运用情况的代码如下:

int processCommand(client *c) {
...
if (server.maxmemory && !server.lua_timedout) {
int out_of_memory = freeMemoryIfNeededAndSafe() == C_ERR;
if (server.current_client == NULL) return C_ERR;
if (out_of_memory &&
(c->cmd->fl郭峰,Redis数据挑选算法,旅行ags & CMD_DENYOOM ||
(c->flags & CLIENT_MULTI && c->cmd->proc != execCommand))) {
flagTransaction(c);
addReply(c, shared.oomerr);
return C_OK;
}
}
...
}

查看内存的运用情况首要经过 freeMemoryIfNeededAndSafe() 函数进行,而 freeMemoryIfNeededAndSafe() 函数终究会调用 freeMemoryIfNeeded() 函数进行处理,因为 freeMemoryIfNeeded() 函数比较巨大,所以咱们分段来进行剖析:

int freeMemoryIfNeeded(void) {
...
size_t mem_reporairtrippted, mem_tofree, mem_freed;
mstime_t latency, eviction_latency;
long long delta;
int slaves = listLength(server.slaves);
...
if (getMaxmemoryState(&mem_reported,NULL,&mem_tofree,NULL) == C_OK)
return C_OK;
mem_freed = 0;
if (server.maxmemory_policy == MAXMEMORY_NO_EVICTION)
goto cant_free;

freeMemoryIfNeeded() 函数首要会调用 getMaxmemoryState() 函数来获取Redis的内存运用情况,假如 getMaxmemoryState() 函数回来 C_OK,表明内存运用总量还没有超出约束,直接回来 C_OK 就能够了。假如 getMaxmemoryState() 函数不是回来 C_OK,表明内存运用总量现已超出约束,需求进行数据挑选,需求挑选数据的巨细经过 mem_tofree 参数回来。

当然,假如装备的挑选战略为 noeviction,表明不能进行数据挑选,所以需求回来 C_ERR 表明有过错。

接着剖析剩下的代码片段:

 latencyStartMonitor(latency);
while (mem_freed < mem_tofree) {
int j, k, i, keys_freed = 0;
static unsigned int next_db = 0;
sds bestkey = NULL;
int bestdbid;
redisDb *db;
dict *dict;
dictEntry *de;
if (server.邓氏鱼maxmemory_policy & (MAXMEMORY_FLAG一去二三里_LRU|MAXMEMORY_FLAG_LFU) ||
server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL)
{
struct evictionPoolEntry *pool = EvictionPoolLRU;
while(bestke皇七子永琮y == NULL) {
unsigned long total_keys = 0, keys;
for (i = 0; i < server.dbnum; i++) {
db = server.db+i;
dict = (server.maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) ?
db->dict : db->expires;
if ((keys = dictSize(dict)) != 0) {
ev八达岭长城门票ictionPoolPopulate(i, dict, db->dict, pool);
total_keys += keys;
}
}
if (!total_keys) break; /* No keys to evict. */
for (k = EVPOOL_SIZE-1; k >= 0; k--) {
if (pool[k].key == NULL) continue;
bestdbid = pool[k].dbid;
if (server.maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) {
de = dictFind(server郭峰,Redis数据挑选算法,旅行.db[pool[k].dbid].dict,
pool[k].key);
} else {
de = dictFind(server.db[pool[k].dbid].expires,
pool[k].key);
}
if (pool[k].key != pool[k].cached)
sdsfree(pool[k].key);
pool[k].key = NULL炉石传说下载;
pool[k].idle = 0;
if (de) {
bestkey = dictGetKey(de);
break;
} else {
/* Ghost... Iterate again. */
}
}
}
郭峰,Redis数据挑选算法,旅行}

假如内存运用总量超出约束,而且装备了挑选战略简靖纹,那么就开端数据挑选进程。在上面的代码中,mem_tofree 变量表明要挑选的数据总量,而 mem_freed 变量表明现已挑选的数据总量。所以在 while 循环中的条件是 mem_freed < mem_tofree,表明挑选的数据总量一定要到达 mem_tofree 停止。

前面介绍过,Redis的挑选战略有许多中,所以进行数据挑选时需求依据装备的战略进行。假如装备的挑选战略是 LRU/LFU/TTL 的话,那么就进入 if 代码块。在 if 代码块里,首要调用 evictionPoolPopulate() 函数挑选一些缓存目标样本放置到 EvictionPoolLRU 数组中。evictionPoolPopulate() 函数后面会进行剖析,现在只需求知道 evictionPoolPopulate() 函数是选取一些缓存目标样本就能够了。

获取到缓存目标样本后,还需求从样本中获取最合适的缓存目标进行挑选,因为在挑选样本时会把最合适的缓存目标放置在 EvictionPoolLRU 数组的尾部,所以只需求从 EvictionPoolLRU 数组的尾部开端查找一个不为空的缓存目标即可。

 else if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM ||
server.maxmemory_policy == MAXMEMORY_VOLATILE_RANDOM)
{
for (i = 0; i < server.dbnum; i++) {
j = (++next_db) % server.dbnum;
db = server.db+j;
dict = (server.maxmemory_policy == MAXMEMORY淮河_ALLKEYS_RANDOM) ?
db->dict : db->expires;
if (dictSize(dict) != 0) {
de = dictGetRandomKey(dict);
bestkey = dictGetKey(de);
bestdbid = j;
break;
}
}
}

假如运用随机挑选战略,那么就进入 else if 代码块,这部分代码的逻辑很简略,假如装备的挑选战略是 volatile-random,那么就从有过期时刻的缓存目标中随机获取,不然就从一切的缓郭峰,Redis数据挑选算法,旅行存目标中随机获取。

 if (bestkey) {
db = server.db+bestdbid;
robj *keyobj = createStringObject(bestkey,sdslen(bestkey));
propagateExpire(db,keyobj,server.lazyfree_lazy_eviction);
delta = (long long) zmalloc_used_memory();
顺德天气预报latencyStartMonitor(eviction_latency);
// 删去缓存目标
if (server.lazyfree_lazy_eviction)
dbAsyncDelete(db,keyobj);
else
dbSyncDelete(db,keyobj);
latencyEndMonitor(eviction_latency);
latencyAddSampleIfNeeded("eviction-del",eviction_latency);
latencyRemoveNestedEvent(latency,eviction_latency);
delta -= (long long) zmalloc_used_memory();
mem_freed += delta;
server.stat_evictedkeys++;
notifyKeyspaceEvent(NOTIFY_EVICTED, "evicted",
keyobj, db->id);
decrRefCount(keyobj);
keys_freed++;
if (slaves) flushSlavesOutp姥姥utBuffers();
if (server.lazyfree_lazy_eviction && !(keys_freed % 16)) {
if (getMaxmemoryState(NULL,NULL,NULL,NULL) == C_OK) {
mem_freed = mem_tofree;
}
}
}

假如找到要挑选的缓存目标,那么就开端开释缓存目标所占用的内存空间。除了需求开释缓存目标占用的内存空间外,还需求进行一些其他的操作,比方把挑选的缓存目标同步到从服务器和把挑选的缓存目标追加到 AOF文件 中等。

当条件 mem_freed < mem_tofree 为假时便会退出 while 循环,阐明Redis的内存运用总量现已小于最大的内存运用约束,freeMemoryIfNeeded() 函数便会回来 C_OK 表明成功履行。

挑选数据样本收集

前面说了,当运用非随机挑选战略时需求进行数据采样(volatile-lru/volatile-lfu/volatile-ttl/allkeys-lru/allkeys-lfu),数据采样经过 eviction郭峰,Redis数据挑选算法,旅行PoolPopulate() 函数进行,因为此函数比较巨大,所以对代码分段剖析:

void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evictionPoolEntry *pool) {
int j, k, count;
dictEntry *samples[server.maxmemory_samples];
count = dictGetSomeKeys(sampledict,samples,server.maxmemory_samples);

evictionPoolPopulate() 函数首要调用 dictGetSomeKeys() 函数从缓存目标调集中获取一些样本,并保存在 samples 数组中。

 for (j = 0; j < count; j++) {
unsigned long long idle;
sds key;
robj *o;
dictEntry *de;
de = samples[j];
key = dictGetKey(de);
if (server.maxmemory_policy != MAXMEMORY_VOLATILE_TTL) {
if (sampledict != keydict) de = dictFind(keydict, key);
o = dictGetVal(de);
}
if (server.maxmemory_policy & MAXMEMORY_FLAG_LRU) {
idle = estimateObjectIdleTime(o);
} else if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
idle = 255-LFUDecrAndReturn(o);
} else if (server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL) {
idle = ULLONG_MAX - (long)dictGetVal(de);
} else {
serverPanic("Unknown eviction policy in evictionPoolPopulate()");
}

上面的代码首要是获取样本缓存目标的排序权值 idel,假如运用 LRU挑选算法,那么就调用 estimateObjectIdleTime() 函数获取排序权值,estimateObjectIdleTime() 函数用于获取缓存目标有多长时刻没有被拜访。排序依照 idle 的值升序排序,就是说 idle 的值越大,就排到越后。

 k = 0;
while (k < EVPOOL_SIZE &&
pool[k].key &&
pool[k].idle < idle) k++;
if (k == 0 && pool[EVPOOL_SIZE-1].key != NULL) {
continue;
} else if (k < EVPOOL_SIZE && pool[k].key == NULL) {
} else {
if (pool[EVPOOL_SIZE-1].key == NULL) {
sds cached = pool[EVPOOL_SIZE-1].cached;
memmove(pool+k+1,pool+k,
sizeof(pool[0])*(EVPOOL_SIZE-k-1));
pool[k].cached = cached;
} else {
k-小毛驴儿歌视频-;
sds cached = pool[0].cached;
if (pool[0].key != pool[0].cached) sdsfree(pool[0].key);
memmove(pool,pool+1,sizeof(po初十ol[0])*k);
pool[k].cached 金首露= cached;
}
}
int klen = sdslen(key);
if (klen > EVPOOL_CACHED_SDS_SIZE) {
pool[k].key = sdsdup(key);
} else {
memcpy(pool[k].cached,key,klen+1);
sdssetlen(pool[k].cached,klen)最佳前男友;
pool[k].key = pool[k].cached;
}
pool[k].idle = idle;
pool[k].dbid = dbid;
}
}

上面这段代码的作用是:依据 idle 的值找到当沦为前缓存目标地点 EvictionPoolLRU 数组的方位,然后把缓存目标保存到 EvictionPoolLRU 数组中。以下插图解说了数据采样的进程:

所以 EvictionPoolLRU 数组的终究一个元素就是最优的挑选缓存目标。

从上面的剖析可知,挑选数据时仅仅从样本中找到最优的挑选缓存目标,并不是从一切缓存目标调集中查找。因为前面介绍的 LRU算法 需求保护一个LRU链表,而保护一个LRU链表的本钱比较大,所以Redis才出此下策。

转载请保留出处和链接!

本文链接:http://www.dujingdian.net/articles/1483.html

文章底部广告(PC版)
文章底部广告(移动版)
百度分享获取地址:http://share.baidu.com/
百度推荐获取地址:http://tuijian.baidu.com/,百度推荐可能会有一些未知的问题,使用中有任何问题请直接联系百度官方客服!
评论框上方广告(PC版)
评论框上方广告(移动版)
推荐阅读
12月11日

盐蒸橙子,暗影光亮,生长懂得,年月之言,你我共识,关于春节的诗

发布 : | 分类 : 国际新闻 | 评论 : 0人 | 浏览 : 276次

人生最怕的,是在某个瞬间忽然读懂一句话,忽然听懂一首歌。成长是个并不容易的过程,道理听过再多遍,也只有真正经历了才懂得。走着走着,明白了很多很多…曾经以为朋友很多,不过就是一群人志同道合。生活总是会用自己的方式告诉你天下没有不散的宴席。...

标签 :
12月05日

宜春,擦干泪水,为自己而活,防盗门

发布 : | 分类 : 国际新闻 | 评论 : 0人 | 浏览 : 141次

有时候我们需要的不是一碗鸡汤,而是一个巴掌。现在你眼睛里留下的泪,都是曾经脑子里进的水!当你学会拒绝别人,学会以牙还牙时,他们反而会尊重你, 甚至敬畏你。我终于相信了那句话:无情一点并没有错。置身事外,谁都可以心平气和,身处其中,谁还可以淡定从容?...

标签 :
12月05日

羊水破了是什么症状,王者荣耀:国服赛末诸葛亮上分技巧攻略,妲己

发布 : | 分类 : 国际新闻 | 评论 : 0人 | 浏览 : 149次

大家好!今天为大家带来王者峡谷中的热门英雄,中单法王诸葛亮的上分攻略。技能介绍:被动技能:命中敌人后增加自己的印记,印记到达5层效果之后,会环绕5颗法球攻击自己身边的敌人,这个被动是很实用的也相当是一个额外的技能。...

标签 :
12月05日

我爱罗,共享:怎么让iPhone/iPad 投屏到电脑上,红色仕途

发布 : | 分类 : 国际新闻 | 评论 : 0人 | 浏览 : 268次

iPhone的投屏功能其实很早之前就有了,相信已经有不少资深果粉已经懂得使用投屏了。小编(果粉之家)也曾分享过投屏操作方法,不过最近有很多人问我该如何投屏?小编只好在拿出来再给大家唠叨一遍。...

标签 :