redis 過期鍵的清除
我們知道了過期時間保存在 expires 字典里, 又知道了該如何判定一個鍵是否過期, 現在剩下的問題是, 如果一個鍵是過期的, 那它什么時候會被刪除?
這個問題有三種可能的答案:
-
定時刪除:在設置鍵的過期時間時,創建一個定時事件,當過期時間到達時,由事件處理器自動執行鍵的刪除操作。
</li> -
惰性刪除:放任鍵過期不管,但是在每次從 dict 字典中取出鍵值時,要檢查鍵是否過期,如果過期的話,就刪除它,并返回空;如果沒過期,就返回鍵值。
</li> -
定期刪除:每隔一段時間,對 expires 字典進行檢查,刪除里面的過期鍵。
</li> </ol>定時刪除定時刪除策略對內存是最友好的: 因為它保證過期鍵會在第一時間被刪除, 過期鍵所消耗的內存會立即被釋放。
這種策略的缺點是, 它對 CPU 時間是最不友好的: 因為刪除操作可能會占用大量的 CPU 時間 —— 在內存不緊張、但是 CPU 時間非常緊張的時候 (比如說,進行交集計算或排序的時候), 將 CPU 時間花在刪除那些和當前任務無關的過期鍵上, 這種做法毫無疑問會是低效的。
除此之外, 目前 Redis 事件處理器對時間事件的實現方式 —— 無序鏈表, 查找一個時間復雜度為 O(N) —— 并不適合用來處理大量時間事件。
惰性刪除惰性刪除對 CPU 時間來說是最友好的: 它只會在取出鍵時進行檢查, 這可以保證刪除操作只會在非做不可的情況下進行 —— 并且刪除的目標僅限于當前處理的鍵, 這個策略不會在刪除其他無關的過期鍵上花費任何 CPU 時間。
惰性刪除的缺點是, 它對內存是最不友好的: 如果一個鍵已經過期, 而這個鍵又仍然保留在數據庫中, 那么 dict 字典和 expires 字典都需要繼續保存這個鍵的信息, 只要這個過期鍵不被刪除, 它占用的內存就不會被釋放。
在使用惰性刪除策略時, 如果數據庫中有非常多的過期鍵, 但這些過期鍵又正好沒有被訪問的話, 那么它們就永遠也不會被刪除(除非用戶手動執行), 這對于性能非常依賴于內存大小的 Redis 來說, 肯定不是一個好消息。
舉個例子, 對于一些按時間點來更新的數據, 比如日志(log), 在某個時間點之后, 對它們的訪問就會大大減少, 如果大量的這些過期數據積壓在數據庫里面, 用戶以為它們已經過期了(已經被刪除了), 但實際上這些鍵卻沒有真正的被刪除(內存也沒有被釋放), 那結果肯定是非常糟糕。
定期刪除從上面對定時刪除和惰性刪除的討論來看, 這兩種刪除方式在單一使用時都有明顯的缺陷: 定時刪除占用太多 CPU 時間, 惰性刪除浪費太多內存。
定期刪除是這兩種策略的一種折中:
它每隔一段時間執行一次刪除操作,并通過限制刪除操作執行的時長和頻率,籍此來減少刪除操作對 CPU 時間的影響。
另一方面,通過定期刪除過期鍵,它有效地減少了因惰性刪除而帶來的內存浪費。
Redis 使用的策略Redis 使用的過期鍵刪除策略是惰性刪除加上定期刪除, 這兩個策略相互配合,可以很好地在合理利用 CPU 時間和節約內存空間之間取得平衡。
因為前面已經說了這兩個策略的概念了,下面兩節就來探討這兩個策略在 Redis 中的具體實現。