redis淘汰策略的几种实现
作者:云养猫v2
redis内存数据数据集大小升到一定大的时候,就会实行数据淘汰策略(回收策略)。
1,volatile-lru:从已设置过期时间的哈希表(server.db[i].expires)中随机挑选多个key,然后在选到的key中用lru算法淘汰最近最少使用的数据
2,allkey-lru:从所有key的哈希表(server.db[i].dict)中随机挑选多个key,然后再选到的key中利用lru算法淘汰最近最少使用的数据
3,volatile-ttl:从已设置过期时间的哈希表(server.db[i].expires)中随机挑选多个key,然后在选到的key中选择过期时间最小的数据淘汰掉。
4,volatile-random:从已设置过期时间的哈希表(server.db[i].expires)中随机挑选key淘汰掉。
5,allkey-random:从所有的key的哈希表(server.db[i].dict)中随机挑数据淘汰
6,no-eviction(驱逐):内存达到上限,不淘汰数据。
redis确认驱逐某个键值对后,会删除这个数据,并将这个数据变更消息发布到本地(AOF持久化)和从机(主从连接)。
LRU数据淘汰机制是这样的:在数据集中随机挑选几个键值对,去除其中最近最少使用的键值对淘汰。所以Redis并不是保证取得所有数据集中最少最少使用的键值对,而只是在随机挑选的几个键值对中。
TTL数据淘汰机制:从国企时间redisDB.expires表中随机挑选几个键值对,取出其中最快过期的键值对淘汰。所以Redis并不保证取得所有过期时间表中最快过期的键值对,而是随机挑选的几个键值对中。
无论是什么机制,都是从所有的键值对中挑选合适的淘汰。
在哪里开始淘汰数据:
Redis服务器每执行一次命令的时候,会检测使用的内存是否超额。如果超额,即进行数据淘汰。
int freeMemoryIfNeeded(void) { /** * noeviction 不淘汰数据,什么都不做 */ if (server.maxmemory_policy == MAXMEMORY_NO_EVICTION) return C_ERR; while (mem_freed < mem_tofree) { int j, k, keys_freed = 0; for (j = 0; j < server.dbnum; j++) { /** * 选择操作的哈希表,Redis另外维护着一个保存过期时间的key=>expire关联的哈希表 */ if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU || server.maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM) { dict = server.db[j].dict; } else { dict = server.db[j].expires; } /** * 分支一:全局哈希表随机或者过期时间哈希表中,随机淘汰一个key */ if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM || server.maxmemory_policy == MAXMEMORY_VOLATILE_RANDOM) { de = dictGetRandomKey(dict); bestkey = dictGetKey(de); } /** * 分支二:全局哈希表随机或者过期时间哈希表中,随机采样多个数据,再运用lru算法挑选一个淘汰 */ else if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU || server.maxmemory_policy == MAXMEMORY_VOLATILE_LRU) { /* 样本集 */ struct evictionPoolEntry *pool = db->eviction_pool; while(bestkey == NULL) { /* * 采样,更新和维护样本集; * 样本集开始是空的,每次操作完并不会清空样本集; * 而且每次采样,都会采集多个数据,同时和样本集中已有的数据进行比较,新增或者更新样本集; */ evictionPoolPopulate(dict, db->dict, db->eviction_pool); /** * 开始对样本集使用lru算法,淘汰样本集中访问时间最晚的key */ for (k = MAXMEMORY_EVICTION_POOL_SIZE-1; k >= 0; k--) { if (pool[k].key == NULL) continue; de = dictFind(dict,pool[k].key); /* 把选取到的key从样本集中移除 */ sdsfree(pool[k].key); memmove(pool+k,pool+k+1, sizeof(pool[0])*(MAXMEMORY_EVICTION_POOL_SIZE-k-1)); pool[MAXMEMORY_EVICTION_POOL_SIZE-1].key = NULL; pool[MAXMEMORY_EVICTION_POOL_SIZE-1].idle = 0; /* pool样本集内的key,只是样本,不一定和db内保持一致,也不必,可能在db中已经被删除的,所以要作判断 */ if (de) { bestkey = dictGetKey(de); break; } else { /* Ghost... */ continue; } } } } /** * 分支三:在设置了过期时间的哈希表里面随机选择多个key,在挑选到的key中选择过期时间最小的一个淘汰掉 */ else if (server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL) { for (k = 0; k < server.maxmemory_samples; k++) { sds thiskey; long thisval; de = dictGetRandomKey(dict); thiskey = dictGetKey(de); thisval = (long) dictGetVal(de); if (bestkey == NULL || thisval < bestval) { bestkey = thiskey; bestval = thisval; } } } if (bestkey) { long long delta; robj *keyobj = createStringObject(bestkey,sdslen(bestkey)); // 命令扩散,把删除key的命令同步到所有从库slave propagateExpire(db,keyobj); // 删除key dbDelete(db,keyobj); } } } return C_OK; }
到此这篇关于redis淘汰策略的几种实现的文章就介绍到这了,更多相关redis淘汰策略内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!