- total:已使用的字节总数,无论是C_OK还是C_ERR都有效 。
- logical:已使用的内存减去slave或AOF缓冲区后的大小,只有返回C_ERR时有效 。
- tofree:需要释放的内存大小,只有返回C_ERR时有效 。
- level:已使用内存的比例,通常是0到1之间,当超出内存限制时,就大于1 。无论是C_OK还是C_ERR都有效 。
首先根据maxmemory_policy进行判断,对于不同的清除策略有不同的实现方法,我们来看LRU的具体实现 。
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) {evictionPoolPopulate(i, dict, db->dict, pool);total_keys += keys;}}首先是填充“过期池”,这里遍历了每一个db(验证了我最开始的想法),调用evictionPoolPopulate函数进行填充 。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);for (j = 0; j < count; j++) {unsigned long long idle;sds key;robj *o;dictEntry *de;de = samples[j];key = dictGetKey(de);/* some code */if (server.maxmemory_policy & MAXMEMORY_FLAG_LRU) {idle = estimateObjectIdleTime(o);}/* some code */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; /* Save SDS before overwriting. */if (pool[0].key != pool[0].cached) sdsfree(pool[0].key);memmove(pool,pool+1,sizeof(pool[0])*k);pool[k].cached = cached;}}/* some code */}}由于篇幅原因,我截取了部分代码,通过这段代码我们可以看到,Redis首先是采样了一部分key,这里采样数量maxmemory_samples通常是5,我们也可以自己设置,采样数量越大,结果就越接近LRU算法的结果,带来的影响是性能随之变差 。采样之后我们需要获得每个key的空闲时间,然后将其填充到“过期池”中的指定位置 。这里“过期池”是按照空闲时间从小到大排序的,也就是说,idle大大key排在最右边 。
填充完“过期池”之后,会从后向前获取到最适合清理的key 。
/* Go backward from best to worst element 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.db[pool[k].dbid].dict,pool[k].key);} else {de = dictFind(server.db[pool[k].dbid].expires,pool[k].key);}/* some code */if (de) {bestkey = dictGetKey(de);break;}}找到需要删除的key后,就需要根据设置清理策略进行同步/异步清理 。if (server.lazyfree_lazy_eviction)dbAsyncDelete(db,keyobj);elsedbSyncDelete(db,keyobj)最后记下本次清理的空间大小,用来在循环条件判断是否要继续清理 。delta -= (long long) zmalloc_used_memory();mem_freed += delta;清理策略最后我们来看一下Redis支持的几种清理策略- noeviction:不会继续处理写请求(DEL可以继续处理) 。
- allkeys-lru:对所有key的近似LRU
- volatile-lru:使用近似LRU算法淘汰设置了过期时间的key
- allkeys-random:从所有key中随机淘汰一些key
- volatile-random:对所有设置了过期时间的key随机淘汰
- volatile-ttl:淘汰有效期最短的一部分key
- volatile-lfu:使用LFU算法淘汰设置了过期时间的key
- allkeys-lfu:从全部key中进行淘汰,使用LFU
关于这次事故的后续处理,我首先是让业务同学回滚了代码,然后让他们使用一个单独的Redis,这样业务再出现类似问题就不会影响到我们的帐号服务了,整体的影响范围也会变得更加可控 。
原文链接:https://www.cnblogs.com/Jackeyzhe/p/12616624.html
【Redis如何清除过期key? 一篇文章带你走近源码!】
推荐阅读
- 如何开一个代购网店 怎样开淘宝店铺代卖
- 干玫瑰花如何喝,茉莉花茶如何喝
- vscode如何优雅的拥抱eslint
- 淘宝联盟怎么推广赚钱技巧 淘宝联盟如何做推广
- 网上如何找到更多的高清图片素材?这6个免版权图库收藏好
- 如何运营10个500人大群?
- 怎样开农村淘宝店 如何开农村淘宝网店
- 粉玫瑰花茶如何泡茶喝,孕妇能喝玫瑰花茶吗
- 华美月饼档次如何?
- 淘宝测图用精准还是广泛 淘宝如何测图测款
