当前位置: 首页 > 后端技术 > PHP

【Redis5源码学习】redis命令expire篇分析

时间:2023-03-29 15:20:18 PHP

Grape命令语法命令含义:为给定key设置生存时间,当key过期(生存时间为0)时,自动删除。命令格式:EXPIREkey秒命令实战:redis>EXPIREcache_page30000#更新过期时间(整数)1返回值:设置成功返回1。当key不存在或者无法为key设置生存时间(比如你尝试在低于2.1.3的Redis版本中更新key的生存时间),返回0。源码分析:expire对应的函数是expireCommand:/*EXPIREkeyseconds*/voidexpireCommand(client*c){//调用通用处理函数expireGenericCommand(c,mstime(),UNIT_SECONDS);}/*这是expire,pexpire、expireat和pexpireat的通用命令实现。因为commad的第二个参数可以是相对的也可以是绝对的,“basetime”参数用来表示什么是basetime(对于command的at变量,或者是相对于expiration的当前时间)。单位为秒或毫秒,仅针对argv[2]参数。基准时间始终以毫秒为单位指定。*/voidexpireGenericCommand(client*c,longlongbasetime,intunit){robj*key=c->argv[1],*param=c->argv[2];长长的时候;/*何时设置为毫秒。*//*取出param中的整数值或者尽量将param中的数据转换成整数值存入when,成功返回OK,返回ERR*/if(getLongLongFromObjectOrReply(c,param,&when,NULL)!=C_OK)返回;/*如果传入的过期时间是秒,则转为毫秒*/if(unit==UNIT_SECONDS)when*=1000;当+=基准时间;/*查询key是否存在,返回信息给客户端*/if(lookupKeyWrite(c->db,key)==NULL){addReply(c,shared.czero);返回;}/**当加载AOF数据时,或者服务器是从节点时,*即使EXPIRE的TTL为负数,或者EXPIREAT提供的时间戳已经过期,*服务器不会主动删除这个key,而是等待来自主节点的显式DEL命令。*/if(when<=mstime()&&!server.loading&&!server.masterhost){//进入该函数的条件:when提供的时间已过,没有数据加载,服务器为主节点(注意主服务器masterhost==NULL)robj*aux;/*删除这个key,这里可以看到del命令的分析,在del命令的分析中,有redis同步和异步删除的策略决策分析,这里不再赘述*/intdeleted=server.lazyfree_lazy_expire?dbAsyncDelete(c->db,key):dbSyncDelete(c->db,key);serverAssertWithInfo(c,键,删除);服务器脏++;/*Replicate/AOFthisasanexplicitDELorUNLINK.*//*将DEL或unlink命令传播到AOF或从服务器*/aux=server.lazyfree_lazy_expire?共享.unlink:共享.del;/*修改客户端的参数数组*/rewriteClientCommandVector(c,2,aux,key);/*发送密钥更改通知*/signalModifiedKey(c->db,key);notifyKeyspaceEvent(NOTIFY_GENERIC,"del",key,c->db->id);addReply(c,shared.cone);返回;}else{//设置key的过期时间//如果服务器是从节点,或者服务器是在loading的时候,根据前面的条件可以推断if至少在slave节点提供的时间到期的时候,会设置//这个猜测是redis中的slave节点不会主动删除操作,除非主节点同步了del命令//那么这个when可能已经过期了setExpire(c,c->db,key,when);addReply(c,shared.cone);signalModifiedKey(c->db,key);notifyKeyspaceEvent(NOTIFY_GENERIC,"expire",key,c->db->id);服务器脏++;返回;}}//设置过期voidsetExpire(client*c,redisDb*db,robj*key,longlongwhen){dictEntry*kde,*de;/*reusethesdsfrommaindictintheexpiredict*//*先从dict中查找,这个过程是根据key从db的dict中查找,先判断是否有安全迭代器,如果没有,rehash,防止哈希表混乱,然后进行hash得到索引值。通过索引我们可以遍历dict的链表来获取值。这里dict上会有相同的hash值。这里*redis使用while循环进行比较,知道key相同就返回此项。*/kde=dictFind(db->dict,key->ptr);serverAssertWithInfo(NULL,key,kde!=NULL);/*dictAddRaw()的变通方法只是多了一个如果有key就返回的过程,如果存在则返回null并返回当前项,否则通过hash索引*index将key添加到dict中返回字典。*/de=dictAddOrFind(db->e??xpires,dictGetKey(kde));/*设置key的过期时间*这里直接用整数值保存过期时间,不是用INT编码的String对象*/dictSetSignedIntegerVal(de,when);intwritable_slave=server.masterhost&&server.repl_slave_ro==0;if(c&&writable_slave&&!(c->flags&CLIENT_MASTER))rememberSlaveKeyWithExpire(db,key);}我们来看一个gdb合法的例子他的流程:首先我们进入gdb的server和cli。我们可以看到进入getLongLongFromObjectOrReply后要做的动作是取出param中的整数值或者尽量将param中的数据转换成整数值。当,当值为我们设置的50000时打印。判断是不是秒,如果是就转为毫秒,然后加上basetime。结果是unix时间转换为2019-09-2305:30:15,(约13小时后)符合预期,下一步就是查找是否有key,如果有则继续下一步。因为我们是正常设置过期时间,所以应该使用else语句来设置。设置是从redisDb中找到我们的key,然后更新过期时间。具体实现看上面的代码分析:接下来就是给客户端发送一个信号。我们继续执行c中的命令,从客户端收到执行成功。与redis相比,此类命令分为三种类型:EXPIREAT、PEXPIRE、PEXPIREATEEXPIREAT的作用与EXPIRE类似,都是用来设置key的生存期。不同的是EXPIREAT命令接受的时间参数是UNIX时间戳(unixtimestamp)。PEXPIRE、PEXPIREAT与EXPIRE、EXPIREAT的区别在于PEXPIRE、PEXPIREAT是以毫秒为单位,而后者以秒为单位。商业用途这里我简单罗列几点,仅供大家参考:限时促销活动信息网站数据缓存(针对一些需要定期更新的数据,比如:积分排行榜)手机验证码限制网站访问频率访客访问(例如:1分钟最多10次访问)