集羣情況下的分佈式鎖
集羣情況下,redis的分佈式鎖有不安全的因素,下面這個場景:
- 主節點擁有鎖
- 客戶端獲取鎖後,主節點掛掉
- 從節點接替,成爲主節點,但是從節點沒有客戶端獲取的鎖
- 新來的客戶端向現在的主節點獲取鎖,會成功,導致出現數據競爭
RedLock
算法可以解決該問題,該算法基本的思想是,加鎖時向過半節點發送set(key, value, nx=True, ex=xxx)
指令,只要半數節點set
成功,則加鎖成功;釋放鎖時,向所有節點發送del
指令。實際的情況還要考慮出錯重試和時漂移等,比較複雜。
該算法可以保證高可用性,但是犧牲性能,而且需要更多的redis實例。
過期策略
定時刪除: Redis把過期的key放在一個單獨的字典中,之後會定時遍歷該字典,然後刪除已經過期的key。集中處理
惰性刪除:客戶端訪問一個key時,redis先檢查key是否過期,如果過期則立刻刪除。零散處理
redis默認每10秒掃描一次過期詞典,定時掃描策略如下:
- 從過期詞典中隨機選出20個key
- 刪除20個key中已經過期的
- 如果過期的key超過,則重複步驟1
爲了防止循環過度,redis默認上限的循環時間時25ms,過時立刻停止,不影響服務。
如果redis中大量的key在同一時刻過期,可能會造成redis卡頓,原因有2個:
- redis持續掃描過期詞典,直到key稀疏
- 內存管理器需要頻繁回收內存頁,產生CPU消耗
服務器如果處於掃描過期狀態,則客戶端請求至少25ms才能得到應答,此時在業務上,會出現大量連接超時關閉的異常。redis的slowlog的慢查詢記錄無法看到,因爲這個記錄只是記錄了執行的時間,不包括等待時間。因此,如果有大批量的key,請設置隨機的超時時間,防止同時過期。
從節點沒有定期掃描機制,過期處理時被動的。主節點的key到期時,會在AOF中追加del
指令,這樣同步到所有的從節點,從節點執行該指令來刪除過期的key
。同步指令異步執行,如果執行不及時,則可能出現數據不一致的情況。
Redis的刪除機制
如果內存超出物理限制,內存數據會和磁盤交換,redis爲了效率,不允許這種情況。
Redis使用類似LRU的算法。因爲LRU算法本身比較消耗內存。隨機LRU給每個key一個24bit的空間,用於表示時間戳。當redis執行寫操作時,如果超過maxmemory限制時,隨機選出5個(可調節)key,然後刪除這些key,如果任然超時,則繼續刪除,直到滿足條件爲止。
懶惰刪除
redis的單線程,不是完全只有一個線程,而是隻有一個主線程處理邏輯等操作,多個異步線程處理異步任務。
ulink key
是把key丟個後臺異步線程刪除。這裏不會有數據競爭,一旦使用該命令,則外界不會獲取到該key了。
flushall async # 異步刪除,同樣的原理,沒有數據競爭。
主線程把數據“刪除”後,對應的key的內存,會進入一個線程安全的異步隊列,由後臺線程完成內存回收。同樣的,AOF
操作也是有自己的任務隊列。