過期策略
如果我們對key設置了失效時間1分鐘,1分鐘后,Redis 是如何對這個 key 進行刪除的呢?
Redis過期策略采用的是惰性刪除+定期刪除策略。
1.惰性刪除
當某個key被設置了過期時間之后,客戶端每次對該key的訪問(讀寫)都會事先檢測該key是否過期,如果過期就直接刪除。
2.定期刪除
當某個key被設置了過期時間之后,客戶端每次對該key的訪問(讀寫)都會事先檢測該key是否過期,如果過期就直接刪除(這種是被動刪除);但有一些鍵只訪問一次或者是冷數據,因此需要主動刪除,默認情況下Redis每秒檢測10次,檢測的對象是所有設置了過期時間的鍵集合,每次從這個集合中隨機檢測20個鍵查看他們是否過期,如果過期就直接刪除,如果過期的key比例超過1/4,那就把上述操作重復一次(貪心算法)。同時為了保證過期掃描不會出現循環過度,導致線程卡死現象,算法還增加了掃描時間的上限,默認不會超過25ms。
如果一個大型的 Redis 實例中所有的 key 在同一時間過期了,會引發什么問題?
這個主動過期 key 的定時任務,是在 Redis 主線程中執行的,也就是說如果在執行主動過期的過程中,出現了需要大量刪除過期 key 的情況,那么此時應用程序在訪問 Redis 時,必須要等待這個過期任務執行結束,Redis 才可以服務這個客戶端請求。此時就會出現,應用訪問 Redis 延時變大。
有小伙伴問,掃描不是有 25ms 的時間上限了么,怎么會導致卡頓呢?
假如有 1001 個客戶端同時將請求發過來了,然后前 1000 個請求的執行時間都是 25ms,那么第 1001 個指令需要等待多久才能執行?25000ms,25秒,這個就是客戶端的卡頓時間,是由服務器不間斷的小卡頓積少成多導致的。
大量key集中過期導致卡頓如何解決?
方案一:在設置 key 的過期時間時,增加一個隨機時間
redis.expireat(key, expire_time + random(300))
這樣一來,Redis 在處理過期時,不會因為集中刪除過多的 key 導致壓力過大,從而避免阻塞主線程。
方案二:Redis 4.0 以上版本,開啟 lazy-free 機制
lazyfree-lazy-expire yes
另外,除了業務層面的優化和修改配置之外,你還可以通過運維手段及時發現這種情況。
運維層面,你需要把 Redis 的各項運行狀態數據監控起來,在 Redis 上執行 INFO 命令就可以拿到這個實例所有的運行狀態數據。
在這里我們需要重點關注 expired_keys 這一項,它代表整個實例到目前為止,累計刪除過期 key 的數量。
你需要把這個指標監控起來,當這個指標在很短時間內出現了突增,需要及時報警出來,然后與業務應用報慢的時間點進行對比分析,確認時間是否一致,如果一致,則可以確認確實是因為集中過期 key 導致的延遲變大。
從庫的過期策略: 從庫不會進行過期掃描,從庫對過期的處理是被動的。主庫在 key 到期時,會在 AOF 文件里增加一條 del 指令,同步到所有的從庫,從庫通過執行這條 del 指令來刪除過期的 key。