expired

更新时间:2022-12-28 05:40:38 阅读: 评论:0


2022年12月28日发(作者:我也爱你英文)

关于Redis数据过期策略

1、Redis中key的的过期时间

通过EXPIREkeyconds命令来设置数据的过期时间。返回1表明设置成功,返回0表明key不存在或者不能成功设置过期时间。在key上设

置了过期时间后key将在指定的秒数后被⾃动删除。被指定了过期时间的key在Redis中被称为是不稳定的。

当key被DEL命令删除或者被SET、GETSET命令重置后与之关联的过期时间会被清除

127.0.0.1:6379>texs201

OK

127.0.0.1:6379>ttls

(integer)17

127.0.0.1:6379>texs2001

OK

127.0.0.1:6379>ttls

(integer)195

127.0.0.1:6379>tranges3100

(integer)6

127.0.0.1:6379>ttls

(integer)152

127.0.0.1:6379>gets

"1x00x00100"

127.0.0.1:6379>ttls

(integer)108

127.0.0.1:6379>getts200

"1x00x00100"

127.0.0.1:6379>gets

"200"

127.0.0.1:6379>ttls

(integer)-1

使⽤PERSIST可以清除过期时间

127.0.0.1:6379>texs100test

OK

127.0.0.1:6379>gets

"test"

127.0.0.1:6379>ttls

(integer)94

127.0.0.1:6379>types

string

127.0.0.1:6379>strlens

(integer)4

127.0.0.1:6379>persists

(integer)1

127.0.0.1:6379>ttls

(integer)-1

127.0.0.1:6379>gets

"test"

使⽤rename只是改了key值

127.0.0.1:6379>expires200

(integer)1

127.0.0.1:6379>ttls

(integer)198

127.0.0.1:6379>renamesss

OK

127.0.0.1:6379>ttlss

(integer)187

127.0.0.1:6379>typess

string

127.0.0.1:6379>getss

"test"

说明:Redis2.6以后expire精度可以控制在0到1毫秒内,key的过期信息以绝对Unix时间戳的形式存储(Redis2.6之后以毫秒级别的精度存

储),所以在多服务器同步的时候,⼀定要同步各个服务器的时间

2、Redis过期键删除策略

Rediskey过期的⽅式有三种:

被动删除:当读/写⼀个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期key

主动删除:由于惰性删除策略⽆法保证冷数据被及时删掉,所以Redis会定期主动淘汰⼀批已过期的key

当前已⽤内存超过maxmemory限定时,触发主动清理策略

被动删除

只有key被操作时(如GET),REDIS才会被动检查该key是否过期,如果过期则删除之并且返回NIL。

1、这种删除策略对CPU是友好的,删除操作只有在不得不的情况下才会进⾏,不会其他的expirekey上浪费⽆谓的CPU时间。

2、但是这种策略对内存不友好,⼀个key已经过期,但是在它被操作之前不会被删除,仍然占据内存空间。如果有⼤量的过期键存在但是⼜

很少被访问到,那会造成⼤量的内存空间浪费。expireIfNeeded(redisDb*db,robj*key)函数位于src/db.c。

/*-----------------------------------------------------------------------------

*ExpiresAPI

*----------------------------------------------------------------------------*/

intremoveExpire(redisDb*db,robj*key){

/*Anexpiremayonlyberemovedifthereisacorrespondingentryinthe

*i,thekeywillneverbefreed.*/

redisAsrtWithInfo(NULL,key,dictFind(db->dict,key->ptr)!=NULL);

returndictDelete(db->expires,key->ptr)==DICT_OK;

}

voidtExpire(redisDb*db,robj*key,longlongwhen){

dictEntry*kde,*de;

/*Reuthesdsfromthemaindictintheexpiredict*/

kde=dictFind(db->dict,key->ptr);

redisAsrtWithInfo(NULL,key,kde!=NULL);

de=dictReplaceRaw(db->expires,dictGetKey(kde));

dictSetSignedIntegerVal(de,when);

}

/*Returntheexpiretimeofthespecifiedkey,or-1ifnoexpire

*isassociatedwiththiskey(isnonvolatile)*/

longlonggetExpire(redisDb*db,robj*key){

dictEntry*de;

/*Noexpire?returnASAP*/

if(dictSize(db->expires)==0||

(de=dictFind(db->expires,key->ptr))==NULL)return-1;

/*Theentrywasfoundintheexpiredict,thismeansitshouldalso

*beprentinthemaindict(safetycheck).*/

redisAsrtWithInfo(NULL,key,dictFind(db->dict,key->ptr)!=NULL);

returndictGetSignedIntegerVal(de);

}

/*PropagateexpiresintoslavesandtheAOFfile.

*Whenakeyexpiresinthemaster,aDELoperationforthiskeyisnt

*toalltheslavesandtheAOFfileifenabled.

*

*Thiswaythekeyexpiryiscentralizedinoneplace,andsinceboth

*AOFandthemaster->slavelinkguaranteeoperationordering,everything

*willbeconsistentevenifweallowwriteoperationsagainstexpiring

*keys.*/

voidpropagateExpire(redisDb*db,robj*key){

robj*argv[2];

argv[0]=;

argv[1]=key;

incrRefCount(argv[0]);

incrRefCount(argv[1]);

if(_state!=REDIS_AOF_OFF)

feedAppendOnlyFile(mand,db->id,argv,2);

replicationFeedSlaves(,db->id,argv,2);

decrRefCount(argv[0]);

decrRefCount(argv[1]);

}

intexpireIfNeeded(redisDb*db,robj*key){

mstime_twhen=getExpire(db,key);

mstime_tnow;

if(when<0)return0;/*Noexpireforthiskey*/

/*Don'bedonelater.*/

if(g)return0;

/*IfweareinthecontextofaLuascript,weclaimthattimeis

*yakeycanexpire

*onlythefirsttimeitisaccesdandnotinthemiddleofthe

*scriptexecution,makingpropagationtoslaves/AOFconsistent.

*Seeissue#1525onGithubformoreinformation.*/

now=_caller?_time_start:mstime();

/*Ifwearerunninginthecontextofaslave,returnASAP:

*theslavekeyexpirationiscontrolledbythemasterthatwill

*ndussynthesizedDELoperationsforexpiredkeys.

*

*Stillwetrytoreturntherightinformationtothecaller,

*thatis,0ifwethinkthekeyshouldbestillvalid,1if

*wethinkthekeyixpiredatthistime.*/

if(host!=NULL)returnnow>when;

/*Returnwhenthiskeyhasnotexpired*/

if(now<=when)return0;

/*Deletethekey*/

_expiredkeys++;

propagateExpire(db,key);

notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED,

"expired",key,db->id);

returndbDelete(db,key);

}

/*-----------------------------------------------------------------------------

*ExpiresCommands

*----------------------------------------------------------------------------*/

/*ThisisthegenericcommandimplementationforEXPIRE,PEXPIRE,EXPIREAT

*ethecommadcondargumentmayberelativeorabsolute

*the"batime"argumentisudtosignalwhatthebatimeis(either0

*for*ATvariantsofthecommand,orthecurrenttimeforrelativeexpires).

*

*unitiitherUNIT_SECONDSorUNIT_MILLISECONDS,andisonlyudfor

*theargv[2]etimeisalwaysspecifiedinmilliconds.*/

voidexpireGenericCommand(redisClient*c,longlongbatime,intunit){

robj*key=c->argv[1],*param=c->argv[2];

longlongwhen;/*unixtimeinmillicondswhenthekeywillexpire.*/

if(getLongLongFromObjectOrReply(c,param,&when,NULL)!=REDIS_OK)

return;

if(unit==UNIT_SECONDS)when*=1000;

when+=batime;

/*Nokey,returnzero.*/

if(lookupKeyRead(c->db,key)==NULL){

addReply(c,);

return;

}

/*EXPIREwithnegativeTTL,orEXPIREATwithatimestampintothepast

*shouldneverbeexecutedasaDELwhenloadtheAOForinthecontext

*ofaslaveinstance.

*

*InsteadwetaketheotherbranchoftheIFstatementttinganexpire

*(possiblyinthepast)andwaitforanexplicitDELfromthemaster.*/

if(when<=mstime()&&!g&&!host){

robj*aux;

redisAsrtWithInfo(c,key,dbDelete(c->db,key));

++;

/*Replicate/AOFthisasanexplicitDEL.*/

aux=createStringObject("DEL",3);

rewriteClientCommandVector(c,2,aux,key);

decrRefCount(aux);

signalModifiedKey(c->db,key);

notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",key,c->db->id);

addReply(c,);

return;

}el{

tExpire(c->db,key,when);

addReply(c,);

signalModifiedKey(c->db,key);

notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"expire",key,c->db->id);

++;

return;

}

}

voidexpireCommand(redisClient*c){

expireGenericCommand(c,mstime(),UNIT_SECONDS);

}

voidexpireatCommand(redisClient*c){

expireGenericCommand(c,0,UNIT_SECONDS);

}

voidpexpireCommand(redisClient*c){

expireGenericCommand(c,mstime(),UNIT_MILLISECONDS);

}

voidpexpireatCommand(redisClient*c){

expireGenericCommand(c,0,UNIT_MILLISECONDS);

}

voidttlGenericCommand(redisClient*c,intoutput_ms){

longlongexpire,ttl=-1;

/*Ifthekeydoesnotexistatall,return-2*/

if(lookupKeyRead(c->db,c->argv[1])==NULL){

addReplyLongLong(c,-2);

return;

}

/*-1ifithasnoexpire,ortheactual

*TTLvalueotherwi.*/

expire=getExpire(c->db,c->argv[1]);

if(expire!=-1){

ttl=expire-mstime();

if(ttl<0)ttl=0;

}

if(ttl==-1){

addReplyLongLong(c,-1);

}el{

addReplyLongLong(c,output_ms?ttl:((ttl+500)/1000));

}

}

voidttlCommand(redisClient*c){

ttlGenericCommand(c,0);

}

voidpttlCommand(redisClient*c){

ttlGenericCommand(c,1);

}

voidpersistCommand(redisClient*c){

dictEntry*de;

de=dictFind(c->db->dict,c->argv[1]->ptr);

if(de==NULL){

addReply(c,);

}el{

if(removeExpire(c->db,c->argv[1])){

addReply(c,);

++;

}el{

addReply(c,);

}

}

}

但仅是这样是不够的,因为可能存在⼀些key永远不会被再次访问到,这些设置了过期时间的key也是需要在过期后被删除的,我们甚⾄可以

将这种情况看作是⼀种内存泄露----⽆⽤的垃圾数据占⽤了⼤量的内存,⽽服务器却不会⾃⼰去释放它们,这对于运⾏状态⾮常依赖于内存的

Redis服务器来说,肯定不是⼀个好消息

主动删除

先说⼀下时间事件,对于持续运⾏的服务器来说,服务器需要定期对⾃⾝的资源和状态进⾏必要的检查和整理,从⽽让服务器维持在⼀个

健康稳定的状态,这类操作被统称为常规操作(cronjob)

在Redis中,常规操作由redis.c/rverCron实现,它主要执⾏以下操作

更新服务器的各类统计信息,⽐如时间、内存占⽤、数据库占⽤情况等。

清理数据库中的过期键值对。

对不合理的数据库进⾏⼤⼩调整。

关闭和清理连接失效的客户端。

尝试进⾏AOF或RDB持久化操作。

如果服务器是主节点的话,对附属节点进⾏定期同步。

如果处于集群模式的话,对集群进⾏定期同步和连接测试。

Redis将rverCron作为时间事件来运⾏,从⽽确保它每隔⼀段时间就会⾃动运⾏⼀次,⼜因为rverCron需要在Redis服务器运⾏期间⼀

直定期运⾏,所以它是⼀个循环时间事件:rverCron会⼀直定期执⾏,直到服务器关闭为⽌。

在Redis2.6版本中,程序规定rverCron每秒运⾏10次,平均每100毫秒运⾏⼀次。从Redis2.8开始,⽤户可以通过修改hz选项来调

整rverCron的每秒执⾏次数,具体信息请参考⽂件中关于hz选项的说明

也叫定时删除,这⾥的“定期”指的是Redis定期触发的清理策略,由位于src/redis.c的activeExpireCycle(void)函数来完成。

rverCron是由redis的事件框架驱动的定位任务,这个定时任务中会调⽤activeExpireCycle函数,针对每个db在限制的时间

REDIS_EXPIRELOOKUPS_TIME_LIMIT内迟可能多的删除过期key,之所以要限制时间是为了防⽌过长时间的阻塞影响redis的正常运

⾏。这种主动删除策略弥补了被动删除策略在内存上的不友好。

因此,Redis会周期性的随机测试⼀批设置了过期时间的key并进⾏处理。测试到的已过期的key将被删除。典型的⽅式为,Redis每秒做10次

如下的步骤:

随机测试100个设置了过期时间的key

删除所有发现的已过期的key

若删除的key超过25个则重复步骤1

这是⼀个基于概率的简单算法,基本的假设是抽出的样本能够代表整个key空间,redis持续清理过期的数据直⾄将要过期的key的百分⽐降

到了25%以下。这也意味着在任何给定的时刻已经过期但仍占据着内存空间的key的量最多为每秒的写操作量除以4.

Redis-3.0.0中的默认值是10,代表每秒钟调⽤10次后台任务。

除了主动淘汰的频率外,Redis对每次淘汰任务执⾏的最⼤时长也有⼀个限定,这样保证了每次主动淘汰不会过多阻塞应⽤请求,以下是这

个限定计算公式:

#defineACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC25/*CPUmax%forkeyscollection*/

...

timelimit=1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC//100;

hz调⼤将会提⾼Redis主动淘汰的频率,如果你的Redis存储中包含很多冷数据占⽤内存过⼤的话,可以考虑将这个值调⼤,但Redis作者建

议这个值不要超过100。我们实际线上将这个值调⼤到100,观察到CPU会增加2%左右,但对冷数据的内存释放速度确实有明显的提⾼(通

过观察keyspace个数和ud_memory⼤⼩)。

可以看出timelimit和是⼀个倒数的关系,也就是说hz配置越⼤,timelimit就越⼩。换句话说是每秒钟期望的主动淘汰频率越⾼,则

每次淘汰最长占⽤时间就越短。这⾥每秒钟的最长淘汰占⽤时间是固定的

250ms(1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/100),⽽淘汰频率和每次淘汰的最长时间是通过hz参数控制的。

从以上的分析看,当redis中的过期key⽐率没有超过25%之前,提⾼hz可以明显提⾼扫描key的最⼩个数。假设hz为10,则⼀秒内最少扫描

200个key(⼀秒调⽤10次*每次最少随机取出20个key),如果hz改为100,则⼀秒内最少扫描2000个key;另⼀⽅⾯,如果过期key⽐率超

过25%,则扫描key的个数⽆上限,但是cpu时间每秒钟最多占⽤250ms。

当REDIS运⾏在主从模式时,只有主结点才会执⾏上述这两种过期删除策略,然后把删除操作”delkey”同步到从结点。

maxmemory

当前已⽤内存超过maxmemory限定时,触发主动清理策略

volatile-lru:只对设置了过期时间的key进⾏LRU(默认值)

allkeys-lru:删除lru算法的key

volatile-random:随机删除即将过期key

allkeys-random:随机删除

volatile-ttl:删除即将过期的

noeviction:永不过期,返回错误当mem_ud内存已经超过maxmemory的设定,对于所有的读写请求,都会触发

redis.c/freeMemoryIfNeeded(void)函数以清理超出的内存。注意这个清理过程是阻塞的,直到清理出⾜够的内存空间。所以如果在达

到maxmemory并且调⽤⽅还在不断写⼊的情况下,可能会反复触发主动清理策略,导致请求会有⼀定的延迟。

当mem_ud内存已经超过maxmemory的设定,对于所有的读写请求,都会触发redis.c/freeMemoryIfNeeded(void)函数以清理超出的内

存。注意这个清理过程是阻塞的,直到清理出⾜够的内存空间。所以如果在达到maxmemory并且调⽤⽅还在不断写⼊的情况下,可能会反

复触发主动清理策略,导致请求会有⼀定的延迟。

清理时会根据⽤户配置的maxmemory-policy来做适当的清理(⼀般是LRU或TTL),这⾥的LRU或TTL策略并不是针对redis的所有key,⽽

是以配置⽂件中的maxmemory-samples个key作为样本池进⾏抽样清理。

maxmemory-samples在redis-3.0.0中的默认配置为5,如果增加,会提⾼LRU或TTL的精准度,redis作者测试的结果是当这个配置为10时已

经⾮常接近全量LRU的精准度了,并且增加maxmemory-samples会导致在主动清理时消耗更多的CPU时间,建议:

尽量不要触发maxmemory,最好在mem_ud内存占⽤达到maxmemory的⼀定⽐例后,需要考虑调⼤hz以加快淘汰,或者进⾏集群

扩容。

如果能够控制住内存,则可以不⽤修改maxmemory-samples配置;如果Redis本⾝就作为LRUcache服务(这种服务⼀般长时间处于

maxmemory状态,由Redis⾃动做LRU淘汰),可以适当调⼤maxmemory-samples。

以下是上⽂中提到的配置参数的说明

#Rediscallsaninternalfunctiontoperformmanybackgroundtasks,like

#closingconnectionsofclientsintimeout,purgingexpiredkeysthatare

#neverrequested,andsoforth.

##Notalltasksareperformedwiththesamefrequency,butRedischecksfor

#taskstoperformaccordingtothespecified"hz"value.

##Bydefault"hz"gthevaluewillumoreCPUwhen

#Redisisidle,butatthesametimewillmakeRedismoreresponsivewhen

#therearemanykeyxpiringatthesametime,andtimeoutsmaybe

#handledwithmoreprecision.

##Therangeisbetween1and500,howeveravalueover100isusuallynot

#ersshoulduthedefaultof10andraithisupto

#100onlyinenvironmentswhereverylowlatencyisrequired.

hz10

#MAXMEMORYPOLICY:howRediswilllectwhattoremovewhenmaxmemory

#lectamongfivebehaviors:

##volatile-lru->removethekeywithanexpiretusinganLRUalgorithm

#allkeys-lru->removeanykeyaccordingtotheLRUalgorithm

#volatile-random->removearandomkeywithanexpiret

#allkeys-random->removearandomkey,anykey

#volatile-ttl->removethekeywiththenearestexpiretime(minorTTL)

#noeviction->don'texpireatall,justreturnanerroronwriteoperations

##Note:withanyoftheabovepolicies,Rediswillreturnanerroronwrite

#operations,whentherearenosuitablekeysforeviction.

##Atthedateofwritingthecommandsare:ttnxtexappend

#incrdecrrpushlpushrpushxlpushxlinrtltrpoplpushsadd

#sintersinterstoresunionsunionstoresdiffsdiffstorezaddzincrby

#zunionstorezinterstorehthtnxhmthincrbyincrbydecrby

#gettmtmtnxexecsort

##Thedefaultis:

#maxmemory-policynoeviction

#LRUandminimalTTLalgorithmsarenotprecialgorithmsbutapproximated

#algorithms(inordertosavememory),soyoucantuneitforspeedor

#aultRediswillcheckfivekeysandpicktheonethatwas

#udlessrecently,youcanchangethesamplesizeusingthefollowing

#configurationdirective.

##Thedefaultof5producesgoodenoughresults.10Approximatesverycloly

#trueLRUbutcostsabitmoreCPU.3isveryfastbutnotveryaccurate.

#maxmemory-samples5

Replicationlink和AOF⽂件中的过期处理

为了获得正确的⾏为⽽不⾄于导致⼀致性问题,当⼀个key过期时DEL操作将被记录在AOF⽂件并传递到所有相关的slave。也即过期删除操

作统⼀在master实例中进⾏并向下传递,⽽不是各salve各⾃掌控。这样⼀来便不会出现数据不⼀致的情形。当slave连接到master后并不能

⽴即清理已过期的key(需要等待由master传递过来的DEL操作),slave仍需对数据集中的过期状态进⾏管理维护以便于在slave被提升为

master会能像master⼀样独⽴的进⾏过期处理。

本文发布于:2022-12-28 05:40:38,感谢您对本站的认可!

本文链接:http://www.wtabcd.cn/fanwen/fan/90/44974.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

上一篇:buckingham palace
下一篇:pigeons
标签:expired
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图