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.