baiyanredis
命令含义:查看指定key的一些信息,通常用于调试或查看内部编码使用
命令格式:数据结构
OBJECT subcommand [key]
OBJECT有4个可选的子命令subcommand:app
命令实战:函数
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.
返回值:REFCOUNT 和 IDLETIME 返回数字;ENCODING 返回相应的编码类型
注:idletime指该键空闲的时间,而空闲指没有被读取也没有被写入。set、get、ttl、expire命令都会重置idletime为0源码分析
整个命令能够分为参数校验、查找字典两步。一样的,object命令的入口是objectCommand():编码
void objectCommand(client *c) { robj *o; // 若是执行OBJECT help命令,打印帮助信息 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); // 直接返回帮助信息 } else if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) { // 若是执行OBJECT refcount key命令 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) // 去键空间中查找该键的robj对象 == NULL) return; addReplyLongLong(c,o->refcount); // 取出robj的refcount字段并返回 } else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) { // 若是执行OBJECT encoding key命令 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) // 去键空间中查找该键的robj对象 == NULL) return; addReplyBulkCString(c,strEncoding(o->encoding)); // 取出robj的encoding字段并返回 } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) { // 若是执行OBJECT idletime key命令 if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))// 去键空间中查找该键的robj对象 == NULL) return; if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) { // 若是开启了LFU淘汰策略,就不会跟踪空转时间,没法使用命令 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) { //若是执行OBJECT freq key命令 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 { // 不是这几种子命令中的一个,直接报错 addReplySubcommandSyntaxError(c); } }
因为OBJECT命令有多个子命令,因此须要进行参数校验,来判断是哪一种子命令类型:调试
... else if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) { // 若是执行OBJECT refcount key命令 ...
不管是哪一种子命令类型,都是访问键的robj结构中的基础字段信息。要想得到这些信息,须要去字典中先去将该键结构查找出来:code
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))== NULL) { return; }
这里经过调用objectCommandLookupOrReply()函数,实现了对键的查找:server
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; // 去字典键空间中查找键 if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL; return (robj*) dictGetVal(de); //根据键查找值,并强转为robj类型 }
找到键以后,直接经过引用robj中的某个字段(如refcount)就可以获得当前键引用计数的信息并返回,命令执行结束:对象
addReplyLongLong(c,o->refcount);
先看下面一个例子:
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"
以上例子代表,redis会根据输入的数据自动选择时间复杂度以及空间复杂度最低的数据结构来存储数据。如上例所示,在foo键的值为1000的时候,redis会选择int结构存储;而在其尾部追加bar以后,其值成为了“1000bar",就没法再使用int类型来存储了,故redis只能退一步使用raw结构来存储。
在切换数据结构的临界点的选择上,redis根据每种底层数据结构的增删改查的时间复杂度及空间复杂度,作了大量的权衡取舍。redis一共有五种基础数据结构,这五种数据结构的编码方式有以下几种选择:
具体的每种数据结构的优缺点咱们已经讨论过,不在此赘述。