Grapehtml
命令含义:移除给定key的生存时间,将这个 key 从『易失的』(带生存时间 key )转换成『持久的』(一个不带生存时间、永不过时的 key )。
命令格式:redis
PERSIST key
命令实战:segmentfault
redis> SET mykey "Hello" OK redis> EXPIRE mykey 10 (integer) 1 redis> TTL mykey (integer) 10 redis> PERSIST mykey (integer) 1 redis> TTL mykey (integer) -1 redis>
返回值:
当生存时间移除成功时,返回 1 .
若是 key 不存在或 key 没有设置生存时间,返回 0 .ide
/* PERSIST key */ void persistCommand(client *c) { //查找这个key,调用lookupKeyWrite函数 if (lookupKeyWrite(c->db,c->argv[1])) { //若是这个key存在,调用删除函数,调用dictGenericDelete函数 if (removeExpire(c->db,c->argv[1])) { addReply(c,shared.cone); server.dirty++; } else { addReply(c,shared.czero); } } else { addReply(c,shared.czero); } }
此处是persist命令的入口,咱们能够看到这个命令在执行的过程当中有一下几个阶段:1.查找你要执行persist命令的key,看是否存在,若是不存在则直接返回客户端信息。若是存在,这调用删除函数移除过时时间,删除成功返回给客户端成功信息,aof标志加1。函数
/* Lookup a key for write operations, and as a side effect, if needed, expires * the key if its TTL is reached. * * Returns the linked value object if the key exists or NULL if the key * does not exist in the specified DB. */ /* 查找这个key */ robj *lookupKeyWrite(redisDb *db, robj *key) { //首先查找这个key是否过时,过时则删除,此函数在以前的几篇文章中已经介绍过,此处不在赘述。 expireIfNeeded(db,key); 在上一个步骤的基础上,若是此键被删除则返回null,不然返回这个键的值 return lookupKey(db,key,LOOKUP_NONE); } int removeExpire(redisDb *db, robj *key) { /* An expire may only be removed if there is a corresponding entry in the * main dict. Otherwise, the key will never be freed. */ serverAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL); return dictDelete(db->expires,key->ptr) == DICT_OK; }
这个过程是在判断这个key是否存在,它分为两个部分,首先他会查找这个key是否过时,若是过时则删除,而后去查询这个key,反之直接查找这个key,此处又一个疑问,为何不是先查找这个key是否存在再去判断是否过时?个人想法:redis中存储过时时间和key-value是两个字典,在expire字典中存在的值必定在key-value字典中存在,那么在expire过时的时候就涉及reids的惰性删除机制,为了知足这个机制,reids在设计的时候会先对key的过时时间作判断,而后再去判断这个key是否存在,此时若是对redis的删除机制感兴趣的话,请查阅关于Redis数据过时策略。源码分析
/* Search and remove an element. This is an helper function for * dictDelete() and dictUnlink(), please check the top comment * of those functions. */ static dictEntry *dictGenericDelete(dict *d, const void *key, int nofree) { uint64_t h, idx; dictEntry *he, *prevHe; int table; //咱们知道在redis中的两个ht,ht[0]和ht[1],若是这两个ht中没有已使用空间,直接return null if (d->ht[0].used == 0 && d->ht[1].used == 0) return NULL; if (dictIsRehashing(d)) _dictRehashStep(d); //根据key来获取他的hash值 h = dictHashKey(d, key); //遍历ht[0]和ht[1],若是找到则删除他的key-value,此处由于是删除过时时间,咱们能够看到外层调用函数传入的是db->expires和key,则此块是从expire的字典中删除, for语句中是删除的过程,很简单,遍历,若是找到了删除,同时释放空间。 for (table = 0; table <= 1; table++) { idx = h & d->ht[table].sizemask; he = d->ht[table].table[idx]; prevHe = NULL; while(he) { if (key==he->key || dictCompareKeys(d, key, he->key)) { /* Unlink the element from the list */ if (prevHe) prevHe->next = he->next; else d->ht[table].table[idx] = he->next; if (!nofree) { dictFreeKey(d, he); dictFreeVal(d, he); zfree(he); } d->ht[table].used--; return he; } prevHe = he; he = he->next; } if (!dictIsRehashing(d)) break; } return NULL; /* not found */ }
此处是redis真正此处过时过时时间的步骤,此处只要设计dict的一些操做,例如遍历dict的ht[0],ht[1]和删除一个dict的值。大体的思路是先找到这个key而后进行释放。由于此处在前面文章已经有所描述,故直接放上传送门:dict是如何find一个元素的。ui