标签:from 因此 slaves 设计方案 shift 极限 col 最好 red
https://blog.csdn.net/Jack__Frost/article/details/72478400?locationNum=13&fps=1
int freeMemoryIfNeeded(void) { size_t mem_used, mem_tofree, mem_freed; int slaves = listLength(server.slaves); /* Remove the size of slaves output buffers and AOF buffer from the * count of used memory. */ //计算占用内存大小时,并不计算slave output buffer和aof buffer,因此maxmemory应该比实际内存小,为这两个buffer留足空间。 mem_used = zmalloc_used_memory(); if (slaves) { listIter li; listNode *ln; listRewind(server.slaves,&li); while((ln = listNext(&li))) { redisClient *slave = listNodeValue(ln); unsigned long obuf_bytes = getClientOutputBufferMemoryUsage(slave); if (obuf_bytes > mem_used) mem_used = 0; else mem_used -= obuf_bytes; } } if (server.appendonly) { mem_used -= sdslen(server.aofbuf); mem_used -= sdslen(server.bgrewritebuf); } //判断已经使用内存是否超过最大使用内存,如果没有超过就返回REDIS_OK, /* Check if we are over the memory limit. */ if (mem_used <= server.maxmemory) return REDIS_OK; //当超过了最大使用内存时,就要判断此时redis到底采用的是那种内存释放策略,根据不同的策略,采取不同的手段。 //(1)首先判断是否是为no-enviction策略,如果是,则返回REDIS_ERR,然后redis就不再接受任何写命令了。 if (server.maxmemory_policy == REDIS_MAXMEMORY_NO_EVICTION) return REDIS_ERR; /* We need to free memory, but policy forbids. */ /* Compute how much memory we need to free. */ mem_tofree = mem_used - server.maxmemory; mem_freed = 0; //(2)接下来就判断淘汰策略是基于所有的键还是只是基于设置了过期时间的键,如果是针对所有的键,就从server.db[j].dict中取数据,如果是针对设置了过期时间的键,就从server.db[j].expires中取数据。 while (mem_freed < mem_tofree) { int j, k, keys_freed = 0; for (j = 0; j < server.dbnum; j++) { long bestval = 0; /* just to prevent warning */ sds bestkey = NULL; struct dictEntry *de; redisDb *db = server.db+j; dict *dict; //(3)然后判断是不是random策略,包括volatile-random 和allkeys-random,这两种策略是最简单的,就是在上面的数据集中随便去一个键,然后删掉。 if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU || server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_RANDOM) { dict = server.db[j].dict; } else { dict = server.db[j].expires; } if (dictSize(dict) == 0) continue; //接着又判断allkeys-random还是volatile-ttl策略 /* volatile-random and allkeys-random policy */ if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_RANDOM || server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_RANDOM) { de = dictGetRandomKey(dict); bestkey = dictGetEntryKey(de); }//如果是random delete,则从dict中随机选一个key //然后就是判断是lru策略还是ttl策略,如果是lru策略就采用lru近似算法 /* volatile-lru and allkeys-lru policy */ else if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU || server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU) { for (k = 0; k < server.maxmemory_samples; k++) { sds thiskey; long thisval; robj *o; de = dictGetRandomKey(dict); thiskey = dictGetEntryKey(de); /* When policy is volatile-lru we need an additonal lookup * to locate the real key, as dict is set to db->expires. */ if (server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU) de = dictFind(db->dict, thiskey); //因为dict->expires维护的数据结构里并没有记录该key的最后访问时间 o = dictGetEntryVal(de); thisval = estimateObjectIdleTime(o); /* Higher idle time is better candidate for deletion */ if (bestkey == NULL || thisval > bestval) { bestkey = thiskey; bestval = thisval; } }//为了减少运算量,redis的lru算法和expire淘汰算法一样,都是非最优解,lru算法是在相应的dict中,选择maxmemory_samples(默认设置是3)份key,挑选其中lru的,进行淘汰 } //如果是ttl策略。ttl策略很简单,就是取maxmemory_samples个键,然后比较他们的过期时间,然后从这些键中找到最快过期的那个键,就是我们将要删除的键。 /* volatile-ttl */ else if (server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_TTL) { for (k = 0; k < server.maxmemory_samples; k++) { sds thiskey; long thisval; de = dictGetRandomKey(dict); thiskey = dictGetEntryKey(de); thisval = (long) dictGetEntryVal(de); /* Expire sooner (minor expire unix timestamp) is better * candidate for deletion */ if (bestkey == NULL || thisval < bestval) { bestkey = thiskey; bestval = thisval; } }//注意ttl实现和上边一样,都是挑选出maxmemory_samples份进行挑选 } //根据不同的策略,我们找到了将要删除的键,下面就是将他们删除的时候了,删除选定的键值对 /* Finally remove the selected key. */ if (bestkey) { long long delta; robj *keyobj = createStringObject(bestkey,sdslen(bestkey)); // 发布数据更新消息,主要是AOF 持久化和从机 propagateExpire(db,keyobj); //将del命令扩散给slaves // 注意, propagateExpire() 可能会导致内存的分配, // propagateExpire() 提前执行就是因为redis 只计算 // dbDelete() 释放的内存大小。倘若同时计算dbDelete() // 释放的内存和propagateExpire() 分配空间的大小,与此 // 同时假设分配空间大于释放空间,就有可能永远退不出这个循环。 // 下面的代码会同时计算dbDelete() 释放的内存和propagateExpire() 分配空间的大小 /* We compute the amount of memory freed by dbDelete() alone. * It is possible that actually the memory needed to propagate * the DEL in AOF and replication link is greater than the one * we are freeing removing the key, but we can‘t account for * that otherwise we would never exit the loop. * * AOF and Output buffer memory will be freed eventually so * we only care about memory used by the key space. */ // 只计算dbDelete() 释放内存的大小 delta = (long long) zmalloc_used_memory(); dbDelete(db,keyobj); delta -= (long long) zmalloc_used_memory(); mem_freed += delta; server.stat_evictedkeys++; decrRefCount(keyobj); keys_freed++; /* When the memory to free starts to be big enough, we may * start spending so much time here that is impossible to * deliver data to the slaves fast enough, so we force the * transmission here inside the loop. */ // 将从机回复空间中的数据及时发送给从机 if (slaves) flushSlavesOutputBuffers(); } }//在所有的db中遍历一遍,然后判断删除的key释放的空间是否足够,未能释放空间,且此时redis 使用的内存大小依旧超额,失败返回 if (!keys_freed) return REDIS_ERR; /* nothing to free... */ } return REDIS_OK; }
从过期时间的表中随机挑选几个键值对,取出其中 ttl 最大的键值对淘汰。同样你会发现,
redis 并不是保证取得所有过期时间的表中最快过期的键值对,而只是随机挑选的几个键值对中的。
//挑选将要过期的数据 else if (server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_TTL) { // server.maxmemory_samples 为随机挑选键值对次数 // 随机挑选server.maxmemory_samples 个键值对,驱逐最快要过期的数据 for (k = 0; k < server.maxmemory_samples; k++) { sds thiskey; long thisval; de = dictGetRandomKey(dict); thiskey = dictGetKey(de); thisval = (long) dictGetVal(de); /* Expire sooner (minor expire unix timestamp) is better * candidate for deletion */ if (bestkey == NULL || thisval < bestval) { bestkey = thiskey; bestval = thisval; } } }
在数据集中随机挑选几个键值对,取出其中 lru 最小的键值对淘汰。所以,你会发现,redis
并不是保证取得所有数据集中最近最少使用(LRU)的键值对,而只是随机挑选的几个键值对中的键值对。
//不同的策略,操作的数据集不同 if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU || server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_RANDOM) { dict = server.db[j].dict; } else {//操作的是设置了过期时间的key集 dict = server.db[j].expires; } if (dictSize(dict) == 0) continue; /* volatile-random and allkeys-random policy */ //随机选择进行淘汰 if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_RANDOM || server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_RANDOM) { de = dictGetRandomKey(dict); bestkey = dictGetKey(de); } /* volatile-lru and allkeys-lru policy */ //具体的LRU算法 else if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU || server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU) { struct evictionPoolEntry *pool = db->eviction_pool; while(bestkey == NULL) { //选择随机样式,并从样本中作用LRU算法选择需要淘汰的数据 evictionPoolPopulate(dict, db->dict, db->eviction_pool); /* Go backward from best to worst element to evict. */ for (k = REDIS_EVICTION_POOL_SIZE-1; k >= 0; k--) { if (pool[k].key == NULL) continue; de = dictFind(dict,pool[k].key); sdsfree(pool[k].key); //将pool+k+1之后的元素向前平移一个单位 memmove(pool+k,pool+k+1, sizeof(pool[0])*(REDIS_EVICTION_POOL_SIZE-k-1)); /* Clear the element on the right which is empty * since we shifted one position to the left. */ pool[REDIS_EVICTION_POOL_SIZE-1].key = NULL; pool[REDIS_EVICTION_POOL_SIZE-1].idle = 0; //选择了需要淘汰的数据 if (de) { bestkey = dictGetKey(de); break; } else { /* Ghost... */ continue; } } } }
127.0.0.1:6379> CONFIG GET maxmemory 1) "maxmemory" 2) "0" 127.0.0.1:6379> CONFIG SET maxmemory 80MB OK 127.0.0.1:6379> CONFIG GET maxmemory 1) "maxmemory" 2) "83886080"
maxmemory 80mb
#如果Hash中字段的数量小于该参数值,Redis将对该Key的Hash Value采用特殊编码。 hash-max-zipmap-entries 64 #如果Hash中各个字段的最大长度不超过512字节,Redis也将对该Key的Hash Value采用特殊编码方式。 hash-max-zipmap-value 512 #下面两个参数的含义基本等同于上面两个和Hash相关的参数,只是作用的对象类型为List。 list-max-ziplist-entries 512 list-max-ziplist-value 64 #如果set中整型元素的数量不超过512时,Redis将会采用该特殊编码。 set-max-intset-entries 512 #如果zset中的元素数量小于128或者各字段长度不超过64,redis会对zset采用特殊编码 zset-max-ziplist-entries 128 zset-max-ziplist-value 64
total_bytes = hash-max-zipmap-entries * hash-max-zipmap-value
标签:from 因此 slaves 设计方案 shift 极限 col 最好 red
原文地址:https://www.cnblogs.com/alsf/p/9399009.html