天天看點

Redis源碼系列之rename講解

作者:Wgrape

一、rename原理

當使用 rename oldKey newKey 指令時,主要會執行如下兩個操作

1、隐式删除newKey

由于rename操作不是renameNX,而是強制性的把舊Key名修改為新Key名。是以如果新Key名指向了資料,Redis就必先把這個資料删掉!

Redis源碼系列之rename講解

注 :key對應的Value抽象為memory記憶體

(1) 源碼

在Redis中,無論執行rename還是renameNX指令,都會執行一個通用的renameGenericCommand函數,隻是傳遞的第二個NX參數不一樣而已,而client參數就是如其名,表示用戶端,用于擷取指令攜帶的參數

  • c->argv[1]表示oldKey
  • c->argv[2]表示newKey

是以核心看renameGenericCommand函數即可

Redis源碼系列之rename講解

如下代碼中 if(lookupKeyWrite(c->db,c->argv[2]) != NULL) ,其中lookupKeyWrite 函數會傳回Key所指向的記憶體指針,如果不為空,則說明已經有資料存儲,是以緊接着就會執行删除newKey的邏輯

Redis源碼系列之rename講解

(2) 時間複雜度

時間複雜度為O(M) ,M為成員數量

(3) 測試

先寫一個有500W成員的Hash類型的bigkey,如下圖發現寫入後,記憶體增加約400MB,删除它需要3秒左右

Redis源碼系列之rename講解
Redis源碼系列之rename講解
Redis源碼系列之rename講解

然後我們執行rename操作,結果如下

Redis源碼系列之rename講解

是以rename操作會隐式的删除newKey,且删除耗時為O(M)

2、修改指針指向

Redis有如下兩種方案可以實作rename效果,第一種是資料拷貝,第二種是修改指針指向。如果采用值拷貝的方式,會增加Redis的記憶體峰值,且拷貝記憶體的時間也會增加耗時,最重要的值拷貝在Redis場景中不需要,是以Redis使用的是第二種修改指針的方式

Redis源碼系列之rename講解

注 :key對應的Value抽象為memory記憶體

(1) 源碼

如下源碼中,在拿到oldKey指向的記憶體對象(值對象)指針後,記為o,然後依次做如下操作

  • 為o引用計數加1,此時o的引用計數為2
  • 把新的鍵值關系(newKey => o)增加到目前DB中,相當于讓newKey重新指向o
  • 删除舊的鍵值(oldKey => o)關系,相當于删除oldKey的指向

由于o的引用計數為2,在删除了oldKey的指向關系後,o的引用計數還是1,并不會觸發GC,是以對象o所占用的記憶體空間仍然是有效的,不過變成了由newKey指向

Redis源碼系列之rename講解

(2) 時間複雜度

O(1)

3、總結

rename操作耗時為O(1)是不準确的,應該為O(M)+O(1)

  • O(M)為删除newKey的耗時,成員與删除耗時成線性關系
  • O(1)為newKey指向新記憶體的耗時,是常數級别,可忽略

二、rename完整過程

  1. find newKey :找到newKey所指向的值對象
  2. delete memory A :删除值對象所指向的記憶體
  3. find oldKey :找到oldKey所指向的值對象
  4. incrRefCount :為oldKey所指向的值對象的引用計數+1
  5. add relation :把(newKey => o)新的鍵值對資訊加到資料庫中,讓newKey指向一個新的值對象
  6. delete relation :删除(oldKey => o)舊的鍵值對資訊,讓oldKey不再指向之前的值對象
Redis源碼系列之rename講解

注 :key對應的Value抽象為memory記憶體

三、關于rename的一些疑問

1、rename具有原子性嗎

從源碼中看,rename過程需要經過删除newKey和修改指針指向這兩步,而如果第二步失敗,第一步操作并不會復原,是以不具有原子性

2、rename中的删除操作是同步的嗎

從代碼中可以看到是同步還是異步,完全取決于配置的DEL機制,即由lazyfree-lazy-server-del配置決定。

Redis源碼系列之rename講解
Redis源碼系列之rename講解

3、如何解決rename耗時長的問題

之前測試中發現rename操作卡了3秒,執行config get *指令,發現确實配置的删除方式為同步删除

Redis源碼系列之rename講解

是以解決方法有兩個,要麼減少Key的member成員數量,要麼配置lazyfree-lazy-server-del為yes

四、結束語

本文已結束,能力有限,文章錯誤地方煩請指出,感謝大家的閱讀

繼續閱讀