[Redis5 Source Learning] Analysis of the persist of redis command

Keywords: PHP Redis

Grape

Command syntax

Command Meaning: Remove the lifetime of a given key and convert it from "volatile" (with lifetime key) to "persistent" (with no lifetime and never expired key).
Command format:

PERSIST key

Command actual combat:

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> 

Return value:
When the lifetime removal is successful, return 1.
If the key does not exist or the key does not set the lifetime, return 0.

Source code analysis

Command entry:

/* PERSIST key */
void persistCommand(client *c) {
    //Find this key and call the lookupKey Write function
    if (lookupKeyWrite(c->db,c->argv[1])) {
            //If the key exists, call the delete function and the dictGenericDelete function
        if (removeExpire(c->db,c->argv[1])) {
            addReply(c,shared.cone);
            server.dirty++;
        } else {
            addReply(c,shared.czero);
        }
    } else {
        addReply(c,shared.czero);
    }
}

Here is the entry of the persist command. We can see that there are several stages in the execution of this command: 1., look for the key that you want to execute the persist command to see if it exists. If it does not exist, it will return directly to the client information. If it exists, the call deletion function removes the expiration time, deletes the success information returned to the client, and adds 1 to the aof flag.

Judging the availability of key

/* 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. */
/* Find this key
*/
robj *lookupKeyWrite(redisDb *db, robj *key) {
    //First, find out if the key is expired, then delete it. This function has been introduced in previous articles, and is not discussed here.
    expireIfNeeded(db,key);
    //Based on the previous step, if the key is deleted, null is returned, otherwise the value of the key is returned.
    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;
}

This process is to determine whether the key exists. It is divided into two parts. First, he will find out if the key is expired. If it expires, delete it and then query the key. Instead, look for the key directly. Here is another question. Why is it not first to find out if this key is there, then to judge whether it is expired? My idea: redis stores expiration time and key-value as two dictionaries. Values in expire dictionary must exist in key-value dictionary. Then when expire expire, it involves the inert deletion mechanism of reids. To satisfy this mechanism, reids will first judge the expiration time of key before deciding whether the key exists or not. If you are interested in redis deletion mechanism, please refer to Redis data expiration strategy.

Real removal process

/* 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;
    
    //We know the two HTS in redis, ht[0] and ht[1], and if there is no space already used in these two hts, return null directly.
    if (d->ht[0].used == 0 && d->ht[1].used == 0) return NULL;
    if (dictIsRehashing(d)) _dictRehashStep(d);
    //Get his hash value by key
    h = dictHashKey(d, key);
    //Traversing through ht[0] and ht[1], if found, delete his key-value, because here is the deletion expiration time, we can see that the outer calling function is passed in DB - > expires and key, then this block is deleted from the dictionary of expire, for statement is the deletion process, simple, traverse, if found deletion, while releasing space.
    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 */
}

Here is the step for redis to actually specify the expiration time here, as long as some operations of dict are designed, such as traversing the ht[0],ht[1] of dict and deleting the value of a dict. The general idea is to find the key and release it. As described in the previous article, put the portal directly: How dict find s an element.

Posted by FrankHarley on Sat, 05 Oct 2019 01:14:43 -0700