redis的关键路径和lazy-free
Redis的寫操作(例如SET,HSET,SADD等)是在關鍵路徑上嗎?
我覺得這需要客戶端根據業務需要來區分:
1、如果客戶端依賴操作返回值的不同,進而需要處理不同的業務邏輯,那么HSET和SADD操作算關鍵路徑,而SET操作不算關鍵路徑。因為HSET和SADD操作,如果field或member不存在時,Redis結果會返回1,否則返回0。而SET操作返回的結果都是OK,客戶端不需要關心結果有什么不同。
2、如果客戶端不關心返回值,只關心數據是否寫入成功,那么SET/HSET/SADD不算關鍵路徑,多次執行這些命令都是冪等的,這種情況下可以放到異步線程中執行。
3、但是有種例外情況,如果Redis設置了maxmemory,但是卻沒有設置淘汰策略,這三個操作也都算關鍵路徑。因為如果Redis內存超過了maxmemory,再寫入數據時,Redis返回的結果是OOM error,這種情況下,客戶端需要感知有錯誤發生才行。
另外,我查閱了lazy-free相關的源碼,發現有很多細節需要補充下:
1、lazy-free是4.0新增的功能,但是默認是關閉的,需要手動開啟。
2、手動開啟lazy-free時,有4個選項可以控制,分別對應不同場景下,要不要開啟異步釋放內存機制:
a) lazyfree-lazy-expire:key在過期刪除時嘗試異步釋放內存
b) lazyfree-lazy-eviction:內存達到maxmemory并設置了淘汰策略時嘗試異步釋放內存
c) lazyfree-lazy-server-del:執行RENAME/MOVE等命令或需要覆蓋一個key時,刪除舊key嘗試異步釋放內存
d) replica-lazy-flush:主從全量同步,從庫清空數據庫時異步釋放內存
3、即使開啟了lazy-free,如果直接使用DEL命令還是會同步刪除key,只有使用UNLINK命令才會可能異步刪除key。
4、這也是最關鍵的一點,上面提到開啟lazy-free的場景,除了replica-lazy-flush之外,其他情況都只是*可能*去異步釋放key的內存,并不是每次必定異步釋放內存的。
開啟lazy-free后,Redis在釋放一個key的內存時,首先會評估代價,如果釋放內存的代價很小,那么就直接在主線程中操作了,沒必要放到異步線程中執行(不同線程傳遞數據也會有性能消耗)。
什么情況才會真正異步釋放內存?這和key的類型、編碼方式、元素數量都有關系(詳細可參考源碼中的lazyfreeGetFreeEffort函數):
a) 當Hash/Set底層采用哈希表存儲(非ziplist/int編碼存儲)時,并且元素數量超過64個
b) 當ZSet底層采用跳表存儲(非ziplist編碼存儲)時,并且元素數量超過64個
c) 當List鏈表節點數量超過64個(注意,不是元素數量,而是鏈表節點的數量,List的實現是在每個節點包含了若干個元素的數據,這些元素采用ziplist存儲)
只有以上這些情況,在刪除key釋放內存時,才會真正放到異步線程中執行,其他情況一律還是在主線程操作。
也就是說String(不管內存占用多大)、List(少量元素)、Set(int編碼存儲)、Hash/ZSet(ziplist編碼存儲)這些情況下的key在釋放內存時,依舊在主線程中操作。
可見,即使開啟了lazy-free,String類型的bigkey,在刪除時依舊有阻塞主線程的風險。所以,即便Redis提供了lazy-free,我建議還是盡量不要在Redis中存儲bigkey。
個人理解Redis在設計評估釋放內存的代價時,不是看key的內存占用有多少,而是關注釋放內存時的工作量有多大。從上面分析基本能看出,如果需要釋放的內存是連續的,Redis作者認為釋放內存的代價比較低,就放在主線程做。如果釋放的內存不連續(大量指針類型的數據),這個代價就比較高,所以才會放在異步線程中去執行。
總結
以上是生活随笔為你收集整理的redis的关键路径和lazy-free的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Consul etcd ZooKeepe
- 下一篇: redis中KEYS替代命令