redis evict.c内存淘汰机制的源码分析
2017-04-12 14:36
881 查看
众所周知,redis是一个内存数据库,所有的键值对都是存储在内存中。当数据变多之后,由于内存有
限就要淘汰一些键值对,使得内存有足够的空间来保存新的键值对。在redis中,通过设置server.maxmemory
来限定内存的使用(server.maxmemory为0,不限制内存),到达server.maxmemory就会触发淘汰机制。redis
主要提供6种淘汰策略:
1)volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
2)volatile-lfu:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最不常使用的数据淘汰
3)volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
4)volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
4)allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
5)allkeys-lfu:从数据集(server.db[i].dict)中挑选最近最不常使用的数据淘汰
6)allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
7)no-enviction(驱逐):禁止驱逐数据
redis在每次执行客户端的命令的时候都会检查使用内存是否超过server.maxmemory,如果超过就进行淘汰数据。
根据淘汰机制从随机选取的键值对中选取键值对构建evictionPool
1)LRU数据淘汰机制:在数据集中随机选取几个键值对,选择lru最大的一部分键值对构建evictionPool。
2)LFU数据淘汰机制:在数据集中随机选取几个键值对,选择lfu最小的一部分键值对构建evictionPool。
3)TTL数据淘汰机制:从设置过期时间的数据集中随机选取几个键值对,选择TTL最大的一部分键值对构建evictionPool。
限就要淘汰一些键值对,使得内存有足够的空间来保存新的键值对。在redis中,通过设置server.maxmemory
来限定内存的使用(server.maxmemory为0,不限制内存),到达server.maxmemory就会触发淘汰机制。redis
主要提供6种淘汰策略:
1)volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
2)volatile-lfu:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最不常使用的数据淘汰
3)volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
4)volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
4)allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
5)allkeys-lfu:从数据集(server.db[i].dict)中挑选最近最不常使用的数据淘汰
6)allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
7)no-enviction(驱逐):禁止驱逐数据
redis在每次执行客户端的命令的时候都会检查使用内存是否超过server.maxmemory,如果超过就进行淘汰数据。
int processCommand(client *c) { ……//server.maxmemory为0,表示对内存没有限制 if (server.maxmemory) { //判断内存,进行内存淘汰 int retval = freeMemoryIfNeeded(); …… } …… }
int freeMemoryIfNeeded(void) { mem_reported = zmalloc_used_memory();//获取redis内存使用 if (mem_reported <= server.maxmemory) return C_OK; mem_used = mem_reported; if (slaves) { listRewind(server.slaves,&li); while((ln = listNext(&li))) { ……//减去slaves的output缓冲区 } }//aof的缓冲区的内存使用 if (server.aof_state != AOF_OFF) { mem_used -= sdslen(server.aof_buf); mem_used -= aofRewriteBufferSize(); } /* Check if we are still over the memory limit. */ if (mem_used <= server.maxmemory) return C_OK; /* Compute how much memory we need to free. */ mem_tofree = mem_used - server.maxmemory; mem_freed = 0; if (server.maxmemory_policy == MAXMEMORY_NO_EVICTION) goto cant_free; /* 禁止驱逐数据 */ //进行数据驱逐 while (mem_freed < mem_tofree) { …… sds bestkey = NULL; if (server.maxmemory_policy & (MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU) || server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL) { //进行ttl或者lru淘汰机制 struct evictionPoolEntry *pool = EvictionPoolLRU; while(bestkey == 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) { evictionPoolPopulate(i, dict, db->dict, pool); //pool根据机制构建的evictionPool } }/*在evictionPool中从后往前选择一个还在存在数据库中的键值进行驱逐*/ 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); } …… if (de) { bestkey = dictGetKey(de); break; } else { /* Ghost... Iterate again. */ } } } } else if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM || server.maxmemory_policy == MAXMEMORY_VOLATILE_RANDOM) { /* 从db->dict或者db->expires随机选择一个键值对进行淘汰*/ 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; } } }//驱逐选中的键值对 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(); if (server.lazyfree_lazy_eviction) dbAsyncDelete(db,keyobj); else dbSyncDelete(db,keyobj); delta -= (long long) zmalloc_used_memory(); mem_freed += delta; server.stat_evictedkeys++; decrRefCount(keyobj); keys_freed++; if (slaves) flushSlavesOutputBuffers(); } } return C_OK; cant_free://进行内存空间的惰性释放 while(bioPendingJobsOfType(BIO_LAZY_FREE)) { if (((mem_reported - zmalloc_used_memory()) + mem_freed) >= mem_tofree) break; usleep(1000); } return C_ERR; }
根据淘汰机制从随机选取的键值对中选取键值对构建evictionPool
1)LRU数据淘汰机制:在数据集中随机选取几个键值对,选择lru最大的一部分键值对构建evictionPool。
2)LFU数据淘汰机制:在数据集中随机选取几个键值对,选择lfu最小的一部分键值对构建evictionPool。
3)TTL数据淘汰机制:从设置过期时间的数据集中随机选取几个键值对,选择TTL最大的一部分键值对构建evictionPool。
void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evictionPoolEntry *pool) { int j, k, count; dictEntry *samples[server.maxmemory_samples]; //从数据集sampledict随机选取键值对 count = dictGetSomeKeys(sampledict,samples,server.maxmemory_samples); for (j = 0; j < count; j++) { 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);//LRU机制,计算lru值 } else if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) { idle = 255-LFUDecrAndReturn(o);//LFU机制,计算lfu值 } else if (server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL) { idle = ULLONG_MAX - (long)dictGetVal(de);//TTL机制,计算ttl值 } k = 0; //根据idle从小到大将键值对插入到pool(插入排序的机制),但只保留idle最大的EVPOOL_SIZE个 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) { /* Inserting into empty position. No setup needed before insert. */ } 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; } } 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; } }
相关文章推荐
- Redis 原理及应用(3)--内存淘汰机制、主从同步原理,HA策略(哨兵机制)分析
- Redis源码分析-内存数据结构intset
- Redis内存淘汰机制
- Nginx源码分析(2)之——共享内存管理之slab机制
- Redis内存淘汰机制
- Redis源码分析(十一)--- memtest内存检测
- redis 源码分析之对象机制和多态
- redis源码分析(1)内存管理
- 【Cocos2d-x 3.x】内存管理机制与源码分析
- Redis 内存淘汰机制
- Redis系列(四)--内存淘汰机制(含单机版内存优化建议)
- redis内存淘汰机制
- redis 内存淘汰机制
- Redis 内存淘汰机制
- Redis 内存淘汰机制
- Redis 内存淘汰机制
- Redis 内存淘汰机制
- Redis内存淘汰机制
- nginx进程间的通信机制源码分析(一)----共享内存
- cocos2d-x源码分析::内存管理机制