Before we get to know the expiration key problem, we need to have a certain understanding of the Redis database and database key space:
struct Redisserver {
// ...
An array that holds all the databases in the server
Redisdb *db;
The number of databases for the server, the value of the Dbnum property is determined by the database option configured by the server, and by default, the value of this option is 16
int dbnum;
// ...
};
Inside the server, the DB attribute of the client state redisclient structure records the client's current target database, which is a pointer to the REDISDB structure:
typedef struct REDISCLIENT {
// ...
Record the database that the client is currently using
Redisdb *db;
// ...
} redisclient;
Now let's take a look at the REDISDB structure, the dict dictionary of the REDISDB structure holds all the key-value pairs in the database, and we refer to this dictionary as the key space:
typedef struct REDISDB {
// ...
//
Database key space, which holds all key-value pairs in the database
Dict *dict;
// ...
} Redisdb;
- Key Space key is the database key, each key is a string object ;
- The value of the key space is the value of the database, and each value can be a string object, a list object, a Hashtable object, a collection object, and any Redis object in an ordered collection object .
Here is an example:
According to this key space, the implementation of related additions, deletions, updates and other operations can be relatively easy to understand, we also ignored here.
Let's introduce another dictionary in the REDISDB structure expires, which holds the expiration time of all the keys in the database, we call this dictionary an outdated dictionary (note that this is the only time the key is expired, Not that the keys in this dictionary are outdated):
- The key of an expired dictionary is a pointer to a key object in the key space (that is, a database key);
- The value of the expired dictionary is a long long integer that holds the expiration time of the database key pointed to by the key-a millisecond-precision Unix timestamp;
With the above knowledge, we can download it to see four commands: Expire, Pexpire, expireat, pexpireat implementation process.
The use of the four commands is relatively simple: EXPIRE <key> <seconds> such as: EXPIRE book 100
Pexpire <key> <millionseconds>
Expireat <key> <timemap>
Pexpireat <key> <timemap> pexpireat book 1388556000000 (January 1, 2014 0 o'clock) In fact I do not know how this is calculated!!!
Note: use the persist command to remove the expiration time of a key.
such as:redis> pexpireat message 1391234400000
(integer) 1
Other commands are similar. But it is worth mentioning that: EXPIRE, Expireat, Pexpire are all converted into pexpireat to achieve. Here's a look at the implementation function for each command:
void Expirecommand (Redisclient *c) {
Expiregenericcommand (C,mstime (), unit_seconds);
}
void Expireatcommand (Redisclient *c) {
Expiregenericcommand (C,0,unit_seconds);
}
void Pexpirecommand (Redisclient *c) {
Expiregenericcommand (C,mstime (), unit_milliseconds);
}
void Pexpireatcommand (Redisclient *c) {
Expiregenericcommand (C,0,unit_milliseconds);
}
They all call the Expiregenericcommand () function for implementation, so let's now analyze how the Expiregenericcommand function is implemented:
/*-----------------------------------------------------------------------------* Expires Commands *------------ ----------------------------------------------------------------*//*the generic command implementation for EXPIRE, Pexpire, Expireat * and Pexpireat. Because the Commad second argument may be relative or absolute * the "basetime" argument are used to signal what the base t IME is (either 0 * for *at variants of the command, or the current time for relative expires). * This function is the underlying implementation function of the EXPIRE, Pexpire, Expireat, and Pexpireat commands. * The second parameter of the command may be an absolute value, or it may be relative. * When executing the *at command, the Basetime is 0, in other cases it holds the current absolute time. * unit is either unit_seconds or unit_milliseconds, and are only used for * the argv[2] parameter. The basetime is always specified in milliseconds. * * unit is used to specify ARGV[2] (incoming expiration time) format, * It can be unit_seconds or unit_milliseconds, * basetime parameter is always in milliseconds format. */voidExpiregenericcommand (Redisclient *c,Long LongBasetime,intunit) {RobJ*key = c->argv[1], *param = c->argv[2]; Long LongWhen;/*Unix time in milliseconds when the key is expire.*/ //Remove the integer value from the Param or attempt to convert the data in the Param to an integer value when there is a when, the successful return REDIS_OK fails to return Redis_err if(Getlonglongfromobjectorreply (c, Param, &when, NULL)! =REDIS_OK)return; //if the incoming expiration time is in seconds, convert it to milliseconds if(unit = = unit_seconds) when *= +; when+=Basetime; /*No key, return zero.*/ //Check to see if the key exists if(Lookupkeyread (c->db,key) = =NULL) {addreply (C,shared.czero); return; } /*EXPIRE with negative TTL, or expireat with a timestamp into the past * should never is executed as a DEL when LOA D the AOF or in the context * of a Slave instance. * * When loading aof data, or when the server is a secondary node, * even if the TTL of EXPIRE is negative, or the timestamp provided by Expireat has expired, * The server will not actively delete this key, but wait for the primary node to send an explicit DEL command. * * Instead We take the other branch of the IF statement setting a expire * (possibly in the past) and wait For a explicit DEL from the master. * * The program will continue to set (a TTL that may have expired) to the expiration time of the key, * and wait for the main node to send the DEL command. */ if(When <= mstime () &&!server.loading &&!server.masterhost) {//When the time provided has expired, the server is the primary node (note the master server's Masterhost==null), and the data is not loadedRobJ *aux; //Delete the keyRedisassertwithinfo (C,key,dbdelete (c->Db,key)); Server.dirty++; /*Replicate/aof this as an explicit DEL.*/ //propagate the DEL command to AOF or from the serverAux = Createstringobject ("DEL",3); //modifying a client's parameter arrayRewriteclientcommandvector (c,2, Aux,key); Decrrefcount (aux); //signal: The key value has changed. Call Touchwatchedkey (Db,key)Signalmodifiedkey (c->Db,key); //Send key space notifications and key event notificationsNotifykeyspaceevent (Redis_notify_generic,"del",key,c->db->ID); Addreply (c, Shared.cone); return; } Else { //set the expiration time of a key//If the server is a secondary node, or if the server is loading,//then there's a chance it's already expired.Setexpire (c->db,key,when); Addreply (C,shared.cone); Signalmodifiedkey (c-Db,key); Notifykeyspaceevent (Redis_notify_generic,"expire",key,c->db->ID); Server.dirty++; return; }}
I'll explain some of the functions I'm not familiar with when I look at the code:
function Purpose: Try to take an integer value from the object o, or try to replace the value in object o with an integer value, and save the resulting value in target. At the same time, if the conversion is removed/turned successfully, return REDIS_OK, otherwise return redis_err, and send an error reply to the client.
Approximate implementation of the process: getlonglongfromobjectorreply-->getlonglongfromobject-->stroll() Finally, the main look at the Stoll function implementation process is OK.
function Purpose: The value in the database for the read operation to take out the key. And updates the hit and miss information in the server based on whether the value was successfully found. Find the return value, return NULL if not found
function implementation
Function Purpose: Modifies the client's parameter array. This involves the use of C language variable parameters can be self-learning.
Function implementation: If you understand the general use of C variable parameters, the function of the implementation process has been generally understood. It is important to note that the lookupcommandororiginal () function, lookupcommandororiginal () is intended to return the correct rediscommand after the command has been renamed.
Signalmodifiedkey (c->db,key) The key value has changed the signal. Call Touchwatchedkey (Db,key) ("Touch" a key, if the key is in a client watch, that the client executes the exec transaction will fail)
Notifykeyspaceevent (Redis_notify_generic, "del",key,c->db-> send key-space notifications and key event notifications (this part of the knowledge will be mentioned in the future)
The main point here is how to set the Expiration key, which can be counted as the next "Redis Expiration key deletion strategy" of the previous or preparatory knowledge.
Redis Learning Notes--expire, Pexpire, expireat, pexpireat execution process