[Redis5 Source Learning] An Analysis of the expire of redis command

Keywords: C Redis Unix less Mobile

Grape

Command syntax

Command Meaning: Set a lifetime for a given key, and when the key expires (lifetime is 0), it will be automatically deleted.
Command format:

EXPIRE key seconds
Command actual combat:

redis> EXPIRE cache_page 30000   # Update expiration time
(integer) 1

Return value:

Setting successfully returns 1.
When the key does not exist or cannot set the lifetime for the key (such as when you try to update the key's lifetime in Edits less than 2.1.3), return 0.

Source code analysis:

The function corresponding to expire is expireCommand:

/* EXPIRE key seconds */
void expireCommand(client *c) {
// Calling the Universal Processing Function
    expireGenericCommand(c,mstime(),UNIT_SECONDS);
}

/*This is the general command implementation of expire, pexpire, expirate and p expireat. Because the second parameter of commad can be relative,
It can also be absolute, so the "base time" parameter is used to indicate what the base time is (for the at variable of the command, or the current time relative to the expiration).
Units are units of seconds or milliseconds and are used only for argv[2] parameters. The base time is always specified in milliseconds.*/
void expireGenericCommand(client *c, long long basetime, int unit) {
    robj *key = c->argv[1], *param = c->argv[2];
    long long when; /*when Set to milliseconds. */

    /* Take out the integer values in param or try to convert the data in param into integer values as much as possible and store them in when.
    Failure to return OK successfully returns ERR*/
    if (getLongLongFromObjectOrReply(c, param, &when, NULL) != C_OK)
        return;

    /*If the incoming expiration time is in seconds, convert it to milliseconds*/
    if (unit == UNIT_SECONDS) when *= 1000;
    when += basetime;

    /* Query if the key exists and no information is returned to the client. */
    if (lookupKeyWrite(c->db,key) == NULL) {
        addReply(c,shared.czero);
        return;
    }

     /*
      * When loading AOF data, or when the server is a satellite node,
      * Even if the TTL of express is negative, or the timestamp provided by express has expired,
      * The server will not actively delete the key, but wait for the main node to send an explicit DEL command.
     */
    if (when <= mstime() && !server.loading && !server.masterhost) {
       //The condition for entering this function is that the time provided by when has expired, the data is not loaded, and the server is the primary node (note that the master host== NULL of the primary server)
        robj *aux;
        /*Delete the key, you can see the del command parsing here, in the del command parsing analysis redis synchronous and asynchronous deletion policy decisions, no more details here*/
        int deleted = server.lazyfree_lazy_expire ? dbAsyncDelete(c->db,key) :
                                                    dbSyncDelete(c->db,key);
        serverAssertWithInfo(c,key,deleted);
        server.dirty++;

        /* Replicate/AOF this as an explicit DEL or UNLINK. */
        /* Propagate DEL or unlink commands to AOF or from servers */
        aux = server.lazyfree_lazy_expire ? shared.unlink : shared.del;
        /*Modify the client's parameter array*/
        rewriteClientCommandVector(c,2,aux,key);
        /* Send key change notification */
        signalModifiedKey(c->db,key);
        notifyKeyspaceEvent(NOTIFY_GENERIC,"del",key,c->db->id);
        addReply(c, shared.cone);
        return;
    } else {
        // Setting key expiration time
        // If the server is a satellite node, or the server is loading, it can be inferred from the conditions in the previous if that at least when the provisioning time expires is set for the satellite node.
        // This guess is that in redis the subordinate node does not take the initiative to delete unless the master node synchronizes the del command.
        // So this when may have expired.
        setExpire(c,c->db,key,when);
        addReply(c,shared.cone);
        signalModifiedKey(c->db,key);
        notifyKeyspaceEvent(NOTIFY_GENERIC,"expire",key,c->db->id);
 
        server.dirty++;
        return;
    }
}

Let's take a legal example of gdb to see its process:

  • First, let's go to the server and cli of gdb

  • We can see that when we enter getLongLongFromObjectOrReply, the action is to take out the integer value in param or try to convert the data in param into integer value as much as possible, and print the value of when as we set it to 50000.

  • To judge whether it is a second, it is converted into milliseconds, and then added basetime. As a result, the unix time is converted to 2019-09-2305:30:15 (after about 13 hours), which meets the expectations. Next step
  • The next step is to find out if there is a key, and the next step is to find out if there is a key.
  • Because we normally set expiration time, we should go to the else statement to set it. The setting is to find our key from redisDb and update expire time:
  • The next step is to send a signal to the client.
  • We continue to execute c commands and receive successful execution from the client

compare

There are three commands for redis: EXPIREAT, PEXPIRE, and PEXPIREAT.
EXPIREAT is similar to EXPIRE in that it is used to set the lifetime for key.
The difference is that the time parameter accepted by the EXPIREAT command is UNIX timestamp.
The difference between PEXPIRE,PEXPIREAT and EXPIRE and EXPIREAT is PEXPIRE. PEXPIREAT is in milliseconds, while the latter is in seconds.

Operational usefulness

Here I simply list a few points for your reference:

  1. Time-limited information on concessional activities
  2. Web site data caching (for some data that needs to be updated regularly, such as: scoreboard)
  3. Mobile phone verification code
  4. Limit the frequency of visitors to the site (e.g. 10 visits per minute at most)

Posted by PGTibs on Sun, 22 Sep 2019 01:15:34 -0700