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一共有五種基礎數據結構,這五種數據結構的編碼方式有以下幾種選擇:
具體的每種數據結構的優缺點咱們已經討論過,不在此贅述。