[Redis5 Source Learning] An Analysis of the object Chapter of the redis command

Keywords: PHP encoding Redis

baiyan

Command usage

Command Meaning: View some information about the specified key, usually for debugging or viewing internal code usage
Command format:

OBJECT subcommand [key]

OBJECT has four optional subcommand s:

  • OBJECT REFCOUNT: View the reference count of the current key
  • OBJECT ENCODING: View the encoding of the current key
  • OBJECT IDLETIME: View the idle time of the current key
  • OBJECT FREQ: View the logarithm of the current key's most recent access frequency
  • OBJECT HELP: View help information for OBJECT commands

Command actual combat:

127.0.0.1:6379> set key1 value1
OK
127.0.0.1:6379> object refcount key1
(integer) 1
127.0.0.1:6379> object encoding key1
"embstr"
127.0.0.1:6379> object idletime key1
(integer) 20
127.0.0.1:6379> object idletime key1
(integer) 23
127.0.0.1:6379> object help
1) OBJECT <subcommand> arg arg ... arg. Subcommands are:
2) ENCODING <key> -- Return the kind of internal representation used in order to store the value associated with a key.
3) FREQ <key> -- Return the access frequency index of the key. The returned integer is proportional to the logarithm of the recent access frequency of the key.
4) IDLETIME <key> -- Return the idle time of the key, that is the approximated number of seconds elapsed since the last access to the key.
5) REFCOUNT <key> -- Return the number of references of the value associated with the specified key.

Return value: REFCOUNT and IDLETIME return numbers; ENCODING return the corresponding encoding type
Note: idletime refers to the time when the key is idle, while idletime refers to the time when the key is not read or written. set, get, ttl, expire commands reset idletime to 0

Source code analysis

The whole command can be divided into two steps: parameter checking and dictionary searching. Similarly, the entry to the object command is objectCommand():

void objectCommand(client *c) {
    robj *o;

    // If the OBJECT help command is executed, print the help information
    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) {
        const char *help[] = {
"ENCODING <key> -- Return the kind of internal representation used in order to store the value associated with a key.",
"FREQ <key> -- Return the access frequency index of the key. The returned integer is proportional to the logarithm of the recent access frequency of the key.",
"IDLETIME <key> -- Return the idle time of the key, that is the approximated number of seconds elapsed since the last access to the key.",
"REFCOUNT <key> -- Return the number of references of the value associated with the specified key.",
NULL
        };
        addReplyHelp(c, help); // Direct return of help information
    } else if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) { // If the OBJECT refcount key command is executed
        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) // Find the robj object of the key in the keyspace
                == NULL) return;
        addReplyLongLong(c,o->refcount); // Remove robj's refcount field and return
    } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) { // If the OBJECT encoding key command is executed
        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) // Find the robj object of the key in the keyspace
                == NULL) return;
        addReplyBulkCString(c,strEncoding(o->encoding)); // Remove the encoding field of robj and return
    } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) { // If the OBJECT idletime key command is executed
        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))// Find the robj object of the key in the keyspace
                == NULL) return;
        if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) { // If the LFU elimination strategy is enabled, the idle time will not be tracked and commands cannot be used.
            addReplyError(c,"An LFU maxmemory policy is selected, idle time not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.");
            return;
        }
        addReplyLongLong(c,estimateObjectIdleTime(o)/1000);
    } else if (!strcasecmp(c->argv[1]->ptr,"freq") && c->argc == 3) { //If the OBJECT freq key command is executed
        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
                == NULL) return;
        if (!(server.maxmemory_policy & MAXMEMORY_FLAG_LFU)) {
            addReplyError(c,"An LFU maxmemory policy is not selected, access frequency not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.");
            return;
        }
        addReplyLongLong(c,LFUDecrAndReturn(o));
    } else { // Not one of these seed commands, direct error reporting
        addReplySubcommandSyntaxError(c);
    }
}

Parameter checking

Because the OBJECT command has multiple subcommands, parameter checking is needed to determine which seed command type is:

...
 else if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) { // If the OBJECT refcount key command is executed
...

Dictionary lookup

Whatever the seed command type is, it is the basic field information in the robj structure of the access key. To get this information, we need to find out the key structure in the dictionary first.

        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))== NULL) {
            return;
        }

By calling the objectCommandLookupOrReply() function, key lookup is realized.

robj *objectCommandLookupOrReply(client *c, robj *key, robj *reply) {
    robj *o = objectCommandLookup(c,key);

    if (!o) addReply(c, reply);
    return o;
}
robj *objectCommandLookup(client *c, robj *key) {
    dictEntry *de;
    // Find keys in the keyspace of the dictionary
    if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL;
    return (robj*) dictGetVal(de); //Look up values based on keys and force them to robj type
}

After the key is found, the information of the current key reference count can be obtained directly by referring to a field in robj (such as refcount) and returned. The command execution ends:

addReplyLongLong(c,o->refcount);

extend

Data structure coding in redis

Let's start with the following example:

redis> set foo 1000
OK
redis> object encoding foo
"int"
redis> append foo bar
(integer) 7
redis> get foo
"1000bar"
redis> object encoding foo
"raw"

The above example shows that redis can automatically select the data structure with the lowest time complexity and space complexity to store data according to the input data. As shown in the example above, when the foo key has a value of 1000, redis will choose the int structure to store; after adding bar to its tail, its value becomes "1000 bar", so it can no longer use the int type to store, so redis can only use the ray structure to store.
In choosing the critical point of switching data structure, redis makes a lot of trade-offs according to the time complexity and space complexity of adding, deleting and modifying each underlying data structure. There are five basic data structures in redis. The coding methods of these five data structures are as follows:

  • Strings can be encoded as int, embstr, or raw. Int is used when strings can be converted to integers, embstr is used for short periods of time, and raw can represent strings of any length.
  • Lists can be coded as ziplist or linkedlist. Ziplist is a special representation for saving smaller list spaces.
  • Sets can be coded as intsets or hashtable. An intset is a special representation of a small set that stores only numbers.
  • Hash tables can be coded as zipmap or hashtable. Zipmap is a special representation of a small hash table.
  • Ordered sets can be coded in ziplist or skiplist formats. Ziplist is used to represent small ordered sets, while skiplist is used to represent ordered sets of any size.

The advantages and disadvantages of each specific data structure have been discussed and will not be discussed here.

Posted by wkoneal on Fri, 27 Sep 2019 02:13:32 -0700