跋山涉水 —— 深刻 Redis 字典遍歷

前言面試

Redis 字典的遍歷過程邏輯比較複雜,互聯網上對這一塊的分析講解很是少。我也花了很多時間對源碼的細節進行了整理,將我我的對字典遍歷邏輯的理解呈現給各位讀者。也許讀者們對字典的遍歷過程有比我更好的理解,還請不吝指教。數組

一邊遍歷一邊修改安全

咱們知道 Redis 對象樹的主幹是一個字典,若是對象不少,這個主幹字典也會很大。當咱們使用 keys 命令搜尋指定模式的 key 時,它會遍歷整個主幹字典。值得注意的是,在遍歷的過程當中,若是知足模式匹配條件的 key 被找到了,還須要判斷 key 指向的對象是否已通過期。若是過時了就須要從主幹字典中將該 key 刪除。架構

那麼,你是否想到了其中的困難之處,在遍歷字典的時候還須要修改字典,會不會出現指針安全問題?函數

重複遍歷學習

字典在擴容的時候要進行漸進式遷移,會存在新舊兩個 hashtable。遍歷須要對這兩個 hashtable 依次進行,先遍歷完舊的 hashtable,再繼續遍歷新的 hashtable。若是在遍歷的過程當中進行了 rehashStep,將已經遍歷過的舊的 hashtable 的元素遷移到了新的 hashtable中,那麼遍歷會不會出現元素的重複?這也是遍歷須要考慮的疑難之處,下面咱們來看看 Redis 是如何解決這個問題的。指針

迭代器的結構對象

Redis 爲字典的遍歷提供了 2 種迭代器,一種是安全迭代器,另外一種是不安全迭代器。blog

迭代器的「安全」指的是在遍歷過程當中能夠對字典進行查找和修改,不用感到擔憂,由於查找和修改會觸發過時判斷,會刪除內部元素。「安全」的另外一層意思是迭代過程當中不會出現元素重複,爲了保證不重複,就會禁止 rehashStep。開發

而「不安全」的迭代器是指遍歷過程當中字典是隻讀的,你不能夠修改,你只能調用 dictNext 對字典進行持續遍歷,不得調用任何可能觸發過時判斷的函數。不過好處是不影響 rehash,代價就是遍歷的元素可能會出現重複。

安全迭代器在剛開始遍歷時,會給字典打上一個標記,有了這個標記,rehashStep 就不會執行,遍歷時元素就不會出現重複。

迭代過程

安全的迭代器在遍歷過程當中容許刪除元素,意味着字典第一維數組下面掛接的鏈表中的元素可能會被摘走,元素的 next 指針就會發生變更,這是否會影響迭代過程呢?下面咱們仔細研究一下迭代函數的代碼邏輯。

值得注意的是在字典擴容時進行rehash,將舊數組中的鏈表遷移到新的數組中。某個具體槽位下的鏈表只可能會遷移到新數組的兩個槽位中。

迭代器的選擇

除了keys指令使用了安全迭代器,由於結果不容許重複。那還有其它的地方使用了安全迭代器麼,什麼狀況下遍歷適合使用非安全迭代器呢?

簡單一點說,那就是若是遍歷過程當中不容許出現重複,那就使用SafeIterator,好比下面的兩種狀況

bgaofrewrite須要遍歷全部對象轉換稱操做指令進行持久化,絕對不容許出現重複

bgsave也須要遍歷全部對象來持久化,一樣不容許出現重複

若是遍歷過程當中須要處理元素過時,須要對字典進行修改,那也必須使用SafeIterator,由於非安全的迭代器是隻讀的。

其它狀況下,也就是容許遍歷過程當中出現個別元素重複,不須要對字典進行結構性修改的狀況下一概使用非安全迭代器。

思考

請繼續思考rehash對非安全遍歷過程的影響,會重複哪些元素,重複的元素會很是多麼仍是隻是少許重複?

歡迎工做一到五年的Java工程師朋友們加入Java架構開發:744677563

本羣提供免費的學習指導 架構資料 以及免費的解答

不懂得問題均可以在本羣提出來 以後還會有職業生涯規劃以及面試指導

相關文章
相關標籤/搜索