【Redis5源碼學習】淺析redis命令之dump篇

Grapehtml


官方文檔

DUMP key
序列化給定 key ,並返回被序列化的值,使用 RESTORE 命令能夠將這個值反序列化爲 Redis 鍵。
序列化生成的值有如下幾個特色:redis

  • 它帶有 64 位的校驗和,用於檢測錯誤, RESTORE 在進行反序列化以前會先檢查校驗和。
  • 值的編碼格式和 RDB 文件保持一致。
  • RDB 版本會被編碼在序列化值當中,若是由於 Redis 的版本不一樣形成 RDB 格式不兼容,那麼 Redis 會拒絕對這個值進行反序列化操做。

序列化的值不包括任何生存時間信息。
可用版本:>= 2.6.0
時間複雜度:
查找給定鍵的複雜度爲 O(1) ,對鍵進行序列化的複雜度爲 O(N*M) ,其中 N 是構成 key 的 Redis 對象的數量,而 M 則是這些對象的平均大小。
若是序列化的對象是比較小的字符串,那麼複雜度爲 O(1) 。
返回值:若是 key 不存在,那麼返回 nil。不然,返回序列化以後的值。網絡

redis> SET greeting "hello, dumping world!"
OK
redis> DUMP greeting
"\x00\x15hello, dumping world!\x06\x00E\xa0Z\x82\xd8r\xc1\xde"
redis> DUMP not-exists-key
(nil)

咱們能夠看到,dump命令就是爲了序列化給定的key。那麼什麼是序列化呢?咱們看下序列化的定義:序列化(Serialization)將對象的狀態信息轉換爲能夠存儲或傳輸的形式的過程。在序列化期間,對象將其當前狀態寫入到臨時或持久性存儲區。之後,能夠經過從存儲區中讀取或反序列化對象的狀態,從新建立該對象。目的是爲了對象能夠跨平臺存儲,和進行網絡傳輸。
命令的使用很簡單,就是dump key,而dump一般和RESTORE配合使用,序列化與反序列化。若是想更多的瞭解序列化相關的知識,推薦閱讀:序列化理解起來很簡單數據結構

源碼分析

首先咱們貼上源碼:app

/* DUMP keyname
 * DUMP is actually not used by Redis Cluster but it is the obvious
 * complement of RESTORE and can be useful for different applications. */
void dumpCommand(client *c) {
    robj *o, *dumpobj;
    rio payload;
    /* 檢查key是否存在 */
    if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) {
    addReply(c,shared.nullbulk);
    return;
    }
    /*建立dump負載. */
    createDumpPayload(&payload,o);
    /* 傳輸給客戶端 */
    dumpobj = createObject(OBJ_STRING,payload.io.buffer.ptr);
    addReplyBulk(c,dumpobj);
    decrRefCount(dumpobj);
    return;
}

接下來咱們慢慢分析。
Dump命令的核心是建立dump負載,因此咱們的核心就在於這個過程,首先咱們簡單描述下大體流程:首先檢查咱們要序列化的key是否存在,若存在則建立dump負載,而後傳輸給客戶端。函數

  1. 檢查key是否存在,主要經過lookupkeyRead來實現,大體就是經過key去redis DB中檢查是否存在,若存在則向下執行,不然向客戶端發送信息。一般咱們在dump一個不存在的key的時候咱們會獲得的結果是一個nil。
  2. 建立dump負載,這塊是dump命令的核心,具體的實現代碼以下:源碼分析

    void createDumpPayload(rio *payload, robj *o) {
     unsigned char buf[2];
     uint64_t crc;
     /* Serialize the object in a RDB-like format. It consist of an object type
        byte followed by the serialized object. This is understood by RESTORE. 
        以相似於rdb的格式序列化對象。它由對象類型字節和序列化對象組成。
     */
     rioInitWithBuffer(payload,sdsempty());
     /*將給定的對象的類型寫到rdb中,失敗報錯*/
     serverAssert(rdbSaveObjectType(payload,o));
     /*將給定的對象寫到rdb中,失敗報錯*/
     serverAssert(rdbSaveObject(payload,o));
     /* Write the footer, this is how it looks like:
     
        ----------------+---------------------+---------------+
        ... RDB payload | 2 bytes RDB version | 8 bytes CRC64 |
        ----------------+---------------------+---------------+
        RDB version and CRC are both in little endian.
     /* RDB版本,被分爲兩個字節保存,表示爲0-65535 */
     buf[0] = RDB_VERSION & 0xff;
     buf[1] = (RDB_VERSION >> 8) & 0xff;
     /*sdscatlen函數是擴展長度,並追加字符串
     payload->io.buffer.ptr = sdscatlen(payload->io.buffer.ptr,buf,2);
     /* 計算CRC校驗碼,共8個字節 */
     crc = crc64(0,(unsigned char*)payload->io.buffer.ptr,
                 sdslen(payload->io.buffer.ptr));
     
     /*對於目標機是大端字節序的機器,進行字節碼的轉換,
     提供了16byte、32byte、64byte字節的轉換。
     在intset\ziplist\zipmap三種數據結構中使用,
     使得不一樣字節序機器生成的rdb文件格式都是統一的(小端字節序),便於兼容。*/
     memrev64ifbe(&crc);
     payload->io.buffer.ptr = sdscatlen(payload->io.buffer.ptr,&crc,8);

    }
    最後生成的序列化對象格式就是下邊這個格式:
    +-------------+---------------------+---------------+
    | RDB payload | 2 bytes RDB version | 8 bytes CRC64 |
    +-------------+---------------------+———————+ui

  3. 發送信息到客戶端,最後一塊咱們能夠認爲是消息傳遞,把序列好的信息傳遞給客戶端。此塊在後邊我會寫一個專題來介紹。

若是讀者有興趣,必定親手gdb試一試!!this

拓展閱讀

相關文章
相關標籤/搜索