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

【Redis5源码学习】redis命令restore的分析

时间:2023-03-29 21:02:11 PHP

葡萄命令语法命令含义:反序列化给定的序列化值,并与给定的键关联。命令格式:RESTOREkeyttlserialized-value[REPLACE][ABSTTL][IDLETIMEseconds][FREQfrequency]命令实战redis>DELmykey0redis>RESTOREmykey0"\n\x17\x17\x00\x00\x00\x12\x00\x00\x00\x03\x00\x00\xc0\x01\x00\x04\xc0\x02\x00\x04\xc0\x03\x00\xff\x04\x00u#<\xc0;.\xe9\xdd"OKredis>TYPEmykeylistredis>LRANGEmykey0-11)"1"2)"2"3)"3"返回值如果反序列化成功则返回OK,否则返回错误。源码分析源码分析部分分为几个部分来讲解。参数据处理voidrestoreCommand(client*c){longlongttl,lfu_freq=-1,lru_idle=-1,lru_clock=-1;里约有效载荷;intj,type,replace=0,absttl=0;抢劫*对象;/*解析参数*/for(j=4;jargc;j++){intadditional=c->argc-j-1;如果(!strcasecmp(c->argv[j]->ptr,"replace")){replace=1;}elseif(!strcasecmp(c->argv[j]->ptr,"absttl")){absttl=1;}elseif(!strcasecmp(c->argv[j]->ptr,"idletime")&&additional>=1&&lfu_freq==-1){if(getLongLongFromObjectOrReply(c,c->argv[j+1],&lru_idle,NULL)!=C_OK)返回;if(lru_idle<0){addReplyError(c,"无效的IDLETIME值,必须>=0");返回;}lru_clock=LRU_CLOCK();j++;/*消耗额外的arg。*/}否则如果(!strcasecmp(c->argv[j]->ptr,"freq")&&additional>=1&&lru_idle==-1){if(getLongLongFromObjectOrReply(c,c->argv[j+1],&lfu_freq,NULL)!=C_OK)返回;if(lfu_freq<0||lfu_freq>255){addReplyError(c,"无效的FREQ值,必须>=0且<=255");返回;}j++;/*消耗额外的arg。*/}else{addReply(c,shared.syntaxerr);返回;}}上面我们提到了restore命令的格式,可以看出第四个参数是可选的,所以解析参数从j=4开始遍历,遍历过程中会根据不同的参数进行不同的操作。下面依次看一下这四个命令:Replace:判断是否为replace字段,将标志replace设置为1。absttl:判断是否为absttl字段,将标志absttl设置为1。如果ABSTTL修饰符为使用时,ttl应表示密钥将终止的绝对Unix时间戳(以毫秒为单位)。Idletime&&freq:这两个参数在对象命令中有详细解释。在objec中,ideltime返回自存储在指定键的对象空闲以来的秒数(未请求读取或写入操作)。freq是一个对数访问频率计数器,它返回存储在指定键处的对象。这条命令解析这两条命令时,ideltime设置lru_clock时钟值。在freq中设置lru_freq,设置频率并判断是否在0-255之间。检查&&对应模式操作/*这里是为了保证key是否存在,只有当replace等于0时才会执行这个操作*/if(!replace&&lookupKeyWrite(c->db,c->argv[1])!=NULL){addReply(c,shared.busykeyerr);返回;}/*检查ttl是否合法,规则是是否小于0,是否可以转换为数字*/if(getLongLongFromObjectOrReply(c,c->argv[2],&ttl,NULL)!=C_OK){返回;}elseif(ttl<0){addReplyError(c,"无效的TTL值,必须>=0");返回;}//检查RDB版本和数据校验和。如果它们不匹配,则返回错误。if(verifyDumpPayload(c->argv[3]->ptr,sdslen(c->argv[3]->ptr))==C_ERR){addReplyError(c,"DUMP负载版本或校验和错误");返回;}rioInitWithBuffer(&payload,c->argv[3]->ptr);if(((type=rdbLoadObjectType(&payload))==-1)||((obj=rdbLoadObject(type,&payload))==NULL)){addReplyError(c,"数据格式错误");返回;}//如果是replace模式,删除keyif(replace)dbDelete(c->db,c->argv[1]);添加密钥/*创建密钥并设置TTL(如果有)*/dbAdd(c->db,c->argv[1],obj);//如果key存在,会报错if(ttl){if(!absttl)ttl+=mstime();setExpire(c,c->db,c->argv[1],ttl);}//创建一个新密钥。//设置ttl是否存在。//如果ttl为0,创建key时不会过期,否则会设置指定的过期时间(毫秒)objectSetLRUOrLFU(obj,lfu_freq,lru_idle,lru_clock);//设置频率和时间范围限制值signalModifiedKey(c->db,c->argv[1]);addReply(c,shared.ok);server.dirty++;}展开LFU最不频繁使用的页面置换算法LeastFrequentlyUsedalgorithm.LFU是先淘汰某段时间内访问次数最少的页面!该算法根据数据的历史访问频率剔除数据。它的核心思想是“如果数据在过去被访问过多次,那么将来访问它的频率会更高”。具体算法过程如下:LRU最长时间未使用。替换算法最近最少使用算法。LRU是最先淘汰最久未被使用的页面!该算法根据数据的历史访问记录来剔除数据。核心思想是“如果数据最近被访问过,那么将来被访问的机会也更高”。具体算法流程如下:FIFO先进先出置换算法FIFO(FirstinFirstout),先入先出。其实在操作系统的设计理念中很多地方都用到了先进先出的思想,比如作业调度(先来先服务)。为什么很多地方都用这个原理呢?因为这个原理简单,符合人的惯性思维,具有公平性,实现起来也简单,所以可以直接使用数据结构中的队列来实现。