关于redis Key淘汰策略的实现方法
1配置文件中的最大内存删除策略
在redis的配置文件中,可以设置redis内存使用的最大值,当redis使用内存达到最大值时(如何知道已达到最大值?),redis会根据配置文件中的策略选取要删除的key,并删除这些key-value的值。若根据配置的策略,没有符合策略的key,也就是说内存已经容不下新的key-value了,但此时有不能删除key,那么这时候写的话,将会出现写错误。
1.1最大内存参数设置
若maxmemory参数设置为0,则分两种情况:
*在64位系统上,表示没有限制。
*在32为系统上,是3G,redis官方文档的说法是,32位系统最大内存是4G,预留1G给系统。而且策略会自动设置为noeviction。
也就是说在32位系统上,若maxmemory设置为0,则默认是3G,当到达3G,再往reidis中写入时,则会报错。
1.2到达最大内存时的几种删除key的策略
* volatile-lru->removethekeywithanexpiresetusinganLRUalgorithm
按照LRU算法(最少最近没使用)来选取,并删除已经设置了expire时间的key。
* allkeys-lru->removeanykeyaccordinglytotheLRUalgorithm
根据LRU算法,删除任意的key。不论这个key是否设置了expire时间。
* volatile-random->removearandomkeywithanexpireset
随机删除一个设置了expire时间的key。
* allkeys-random->removearandomkey,anykey
随机删除任意一个key,不论这个key是否设置了expire时间。
* volatile-ttl->removethekeywiththenearestexpiretime(minorTTL)
删除具有最近终止时间值(TTL)的key。
* noeviction->don'texpireatall,justreturnanerroronwriteoperations
若没有设置终止时间,返回一个错误。
1.3配置内存最大值策略
以下这些命令的默认策略是:volatile-lru
#Atthedateofwritingthiscommandsare:setsetnxsetexappend
#incrdecrrpushlpushrpushxlpushxlinsertlsetrpoplpushsadd
#sintersinterstoresunionsunionstoresdiffsdiffstorezaddzincrby
#zunionstorezinterstorehsethsetnxhmsethincrbyincrbydecrby
#getsetmsetmsetnxexecsort
#
#Thedefaultis:
#maxmemory-policyvolatile-lru
1.4配置要删除key的检测样本个数
maxmemory-samples
由于LRU和最小TTL算法都是不是精确的算法。因此你可以选择要检测样本的个数。例如,默认情况下redis将会检查3个key,并从这3个key中选取一个最近没有使用的key。当然你可以修改检查样本的个数的值。
要修改这个值,可以通过在配置文件中设置参数:
maxmemory-samples3
2实现
这几种删除策略的实现都是在函数freeMemoryIfNeeded(void)中完成的。下面具体讲解每种策略是如何实现的。
2.1什么时候去删除key-value
当设置了maxmemory-policy策略后,什么时候会去删除key呢?
实际上,当设置了maxmemory参数后,在处理每个命令的时候都会根据maxmemory-policy去删除对应的key值。
代码如下:
//处理客户端的每个命令,都会调用这个函数 intprocessCommand(redisClient*c){ ...... /*Handlethemaxmemorydirective. * *Firstwetrytofreesomememoryifpossible(iftherearevolatile *keysinthedataset).Iftherearenottheonlythingwecando *isreturninganerror.*/ //以上意思是:若存在可以删除的key,就释放一些内存,若不存在,给客户端返回一个错误。 if(server.maxmemory){//若maxmemory不为0,则调用以下函数,释放其中一些key intretval=freeMemoryIfNeeded();//根据配置策略删除key if((c->cmd->flags&REDIS_CMD_DENYOOM)&&retval==REDIS_ERR){//若出错,就终止处理命令,把错误返回给客户端 flagTransaction(c); addReply(c,shared.oomerr); returnREDIS_OK; } } ...... }
实战1:若没有设置maxmemory变量,即使设置了maxmemory-policy,也不会起作用。
实战2:若没有设置maxmemory变量,在处理命令时将不会调用释放策略,会加速命令的处理过程。
2.2删除key的总体流程
当内存达到最大值时需要按策略删除老的key,所有的删除操作和删除策略的实现都是在函数freeMemoryIfNeeded()中实现的。
在执行删除策略之前,先要选取db和查找key。
总体步骤如下:
intfreeMemoryIfNeeded(void){ size_tmem_used,mem_tofree,mem_freed; intslaves=listLength(server.slaves); mstime_tlatency; /*RemovethesizeofslavesoutputbuffersandAOFbufferfromthe *countofusedmemory.*/ mem_used=zmalloc_used_memory(); if(slaves){ listIterli; listNode*ln; listRewind(server.slaves,&li); while((ln=listNext(&li))){ redisClient*slave=listNodeValue(ln); unsignedlongobuf_bytes=getClientOutputBufferMemoryUsage(slave); if(obuf_bytes>mem_used) mem_used=0; else mem_used-=obuf_bytes; } } if(server.aof_state!=REDIS_AOF_OFF){ mem_used-=sdslen(server.aof_buf); mem_used-=aofRewriteBufferSize(); } /*Checkifweareoverthememorylimit.*/ //检查目前系统是否超过内存的限制 if(mem_used<=server.maxmemory)returnREDIS_OK; if(server.maxmemory_policy==REDIS_MAXMEMORY_NO_EVICTION) returnREDIS_ERR;/*Weneedtofreememory,butpolicyforbids.*/ /*Computehowmuchmemoryweneedtofree.*/ mem_tofree=mem_used-server.maxmemory; mem_freed=0; latencyStartMonitor(latency); while(mem_freed2.2volatile-lru机制和allkeys-lru的实现
2.2.1redis中的LRU机制
对于LRU机制,redis的官方文档有这样的解释:
RedisLRUalgorithmisnotanexactimplementation.ThismeansthatRedisisnotabletopickthebestcandidateforeviction,thatis,theaccessthatwasaccessedthemostinthepast.InsteaditwilltrytorunanapproximationoftheLRUalgorithm,bysamplingasmallnumberofkeys,andevictingtheonethatisthebest(withtheoldestaccesstime)amongthesampledkeys. HoweversinceRedis3.0(thatiscurrentlyinbeta)thealgorithmwasimprovedtoalsotakeapoolofgoodcandidatesforeviction.Thisimprovedtheperformanceofthealgorithm,makingitabletoapproximatemorecloselythebehaviorofarealLRUalgorithm. WhatisimportantabouttheRedisLRUalgorithmisthatyouareabletotunetheprecisionofthealgorithmbychangingthenumberofsamplestocheckforeveryeviction.Thisparameteriscontrolledbythefollowingconfigurationdirective: maxmemory-samples5 ThereasonwhyRedisdoesnotuseatrueLRUimplementationisbecauseitcostsmorememory.HowevertheapproximationisvirtuallyequivalentfortheapplicationusingRedis.ThefollowingisagraphicalcomparisonofhowtheLRUapproximationusedbyRediscompareswithtrueLRU.大意是说,redis的LRU算法不是正真意思上的LRU。而是使用另外一种方式实现的。也就意味着,redis并不能每次都选择一个最好的key来删除。没有使用正真的LRU算法的原因是,它可能会消耗更多的内存。该算法和正真的LRU算法效果大概相同。
redis是在一小部分key中选择最优的要删除的key。这一小部分key的个数可以指定,可以在配置文件中设置参数maxmemory-samples。
2.2.2LRU机制的实现
freeMemoryIfNeeded()函数,首先要计算最大空余内存和目前已经使用的内存大差值,若不够了,就要释放老的key-value。
若使用的是LRU策略,就会走以下代码,先进行最优删除key的选择,然后进行删除操作:
intfreeMemoryIfNeeded(void){ size_tmem_used,mem_tofree,mem_freed; intslaves=listLength(server.slaves); mstime_tlatency; /*RemovethesizeofslavesoutputbuffersandAOFbufferfromthe *countofusedmemory.*/ mem_used=zmalloc_used_memory();//计算目前使用的内存大小,要排除slave和AOF使用的buffer大小 if(slaves){//遍历slaves链表,减去slave使用的内存数量 listIterli; listNode*ln; listRewind(server.slaves,&li); while((ln=listNext(&li))){ redisClient*slave=listNodeValue(ln); unsignedlongobuf_bytes=getClientOutputBufferMemoryUsage(slave); if(obuf_bytes>mem_used) mem_used=0; else mem_used-=obuf_bytes; } } if(server.aof_state!=REDIS_AOF_OFF){//减去AOF使用的内存大小 mem_used-=sdslen(server.aof_buf); mem_used-=aofRewriteBufferSize(); } /*Checkifweareoverthememorylimit.*///检查是否达到设置的内存上限 if(mem_used<=server.maxmemory)returnREDIS_OK; //不释放内存 if(server.maxmemory_policy==REDIS_MAXMEMORY_NO_EVICTION) returnREDIS_ERR;/*Weneedtofreememory,butpolicyforbids.*/ /*Computehowmuchmemoryweneedtofree.*///计算要释放的内存量 mem_tofree=mem_used-server.maxmemory; mem_freed=0; latencyStartMonitor(latency); while(mem_freedexpires.*/ //若最大内存删除策略是volatile-lru,则需要从db中查找thiskey。 //若是VOLATILE-xx策略,则目前操作的库的存储结构是expires,此时需要从dict中找到该key if(server.maxmemory_policy==REDIS_MAXMEMORY_VOLATILE_LRU) de=dictFind(db->dict,thiskey); //获取keyde的value值 o=dictGetVal(de); //查看该key的剩下的生存时间 thisval=estimateObjectIdleTime(o); /*Higheridletimeisbettercandidatefordeletion*/ //每次都从遍历的几个Key中选出lru最长的key。 //那么如何更新key的lru值呢?每次查找该key的时候就会更新该key的lru值,该值是系统的时间戳。 if(bestkey==NULL||thisval>bestval){ bestkey=thiskey; bestval=thisval; } } } /*volatile-ttl*/ elseif(server.maxmemory_policy==REDIS_MAXMEMORY_VOLATILE_TTL){ for(k=0;k id); decrRefCount(keyobj); keys_freed++; /*Whenthememorytofreestartstobebigenough,wemay *startspendingsomuchtimeherethatisimpossibleto *deliverdatatotheslavesfastenough,soweforcethe *transmissionhereinsidetheloop.*/ if(slaves)flushSlavesOutputBuffers(); } } if(!keys_freed){ latencyEndMonitor(latency); latencyAddSampleIfNeeded("eviction-cycle",latency); returnREDIS_ERR;/*nothingtofree...*/ } } latencyEndMonitor(latency); latencyAddSampleIfNeeded("eviction-cycle",latency); returnREDIS_OK; } 以上这篇关于redisKey淘汰策略的实现方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。