redis系列之------過時策略

前言

咱們都知道redis是常駐在內存當中的,所以他的效率比MySQL要快不少不少。但又引起了另一個問題,內存從本質上講,它是昂貴的,不能用於大量的長時間的存儲,他是「不安全不穩定的「,而且有可能存在內存泄露,不能與磁盤相比。redis

那麼若是解決這種問題呢?所以咱們使用redis的時候,強制的應該給每一個Key加上過時時間。咱們來看看redis對過時的Key是怎麼處理的。數據庫

過時鍵的斷定

第一個問題,redis如何知道他是一個過時鍵呢?又該如何斷定他過時了呢?安全

在數據庫中, 全部鍵的過時時間都被保存在 redisDb 結構的 expires 字典裏:性能

1 typedef struct redisDb {
2 
3     // ...
4 
5     dict *expires;
6 
7     // ...
8 
9 } redisDb;

expires 字典的鍵是一個指向 dict 字典(鍵空間)裏某個鍵的指針, 而字典的值則是鍵所指向的數據庫鍵的到期時間, 這個值以 long long類型表示。spa

下圖展現了一個含有三個鍵的數據庫,其中 number 和 book 兩個鍵帶有過時時間unix

 

咱們能夠看到number和book是有一個過時時間的,他是long long類型。實則他是一個unix的時間戳,所以判斷他是否過時就十分的簡單了。指針

經過 expires 字典, 能夠用如下步驟檢查某個鍵是否過時:日誌

  1. 檢查鍵是否存在於 expires 字典:若是存在,那麼取出鍵的過時時間;
  2. 檢查當前 UNIX 時間戳是否大於鍵的過時時間:若是是的話,那麼鍵已通過期;不然,鍵未過時。

能夠用僞代碼來描述這一過程:code

 1 def is_expired(key):
 2 
 3     # 取出鍵的過時時間
 4     key_expire_time = expires.get(key)
 5 
 6     # 若是過時時間不爲空,而且當前時間戳大於過時時間,那麼鍵已通過期
 7     if expire_time is not None and current_timestamp() > key_expire_time:
 8         return True
 9 
10     # 不然,鍵未過時或沒有設置過時時間
11     return False

過時鍵的清除

當咱們知道這個鍵過時了,咱們該如何清除呢?基本上有如下三種策略:xml

  • 定時刪除:在設置鍵的過時時間時,建立一個定時事件,當過時時間到達時,由事件處理器自動執行鍵的刪除操做。
  • 惰性刪除:聽任鍵過時無論,可是在每次從 dict 字典中取出鍵值時,要檢查鍵是否過時,若是過時的話,就刪除它,並返回空;若是沒過時,就返回鍵值。
  • 按期刪除:每隔一段時間,對 expires 字典進行檢查,刪除裏面的過時鍵。

定時刪除

定時刪除策略對內存是最友好的: 由於它保證過時鍵會在第一時間被刪除, 過時鍵所消耗的內存會當即被釋放。

這種策略的缺點是, 它對 CPU 時間是最不友好的: 由於刪除操做可能會佔用大量的 CPU 時間 —— 在內存不緊張、可是 CPU 時間很是緊張的時候 (好比說,進行交集計算或排序的時候), 將 CPU 時間花在刪除那些和當前任務無關的過時鍵上, 這種作法毫無疑問會是低效的。

除此以外, 目前 Redis 事件處理器對時間事件的實現方式 —— 無序鏈表, 查找一個時間複雜度爲 O(N) —— 並不適合用來處理大量時間事件。

惰性刪除

惰性刪除對 CPU 時間來講是最友好的: 它只會在取出鍵時進行檢查, 這能夠保證刪除操做只會在非作不可的狀況下進行 —— 而且刪除的目標僅限於當前處理的鍵, 這個策略不會在刪除其餘無關的過時鍵上花費任何 CPU 時間。

惰性刪除的缺點是, 它對內存是最不友好的: 若是一個鍵已通過期, 而這個鍵又仍然保留在數據庫中, 那麼 dict 字典和 expires 字典都須要繼續保存這個鍵的信息, 只要這個過時鍵不被刪除, 它佔用的內存就不會被釋放。

在使用惰性刪除策略時, 若是數據庫中有很是多的過時鍵, 但這些過時鍵又正好沒有被訪問的話, 那麼它們就永遠也不會被刪除(除非用戶手動執行), 這對於性能很是依賴於內存大小的 Redis 來講, 確定不是一個好消息。

舉個例子, 對於一些按時間點來更新的數據, 好比日誌(log), 在某個時間點以後, 對它們的訪問就會大大減小, 若是大量的這些過時數據積壓在數據庫裏面, 用戶覺得它們已通過期了(已經被刪除了), 但實際上這些鍵卻沒有真正的被刪除(內存也沒有被釋放), 那結果確定是很是糟糕。

按期刪除

從上面對定時刪除和惰性刪除的討論來看, 這兩種刪除方式在單一使用時都有明顯的缺陷: 定時刪除佔用太多 CPU 時間, 惰性刪除浪費太多內存。

按期刪除是這兩種策略的一種折中:

  • 它每隔一段時間執行一次刪除操做,並經過限制刪除操做執行的時長和頻率,籍此來減小刪除操做對 CPU 時間的影響。
  • 另外一方面,經過按期刪除過時鍵,它有效地減小了因惰性刪除而帶來的內存浪費。

所以最終redis使用的過時鍵刪除策略是惰性刪除加上按期刪除, 這兩個策略相互配合,能夠很好地在合理利用 CPU 時間和節約內存空間之間取得平衡。

所以redis大體流程以下:獲取key以前,會檢查key是否過時,如過時,直接刪除,返回null。

而且會按期的隨機的檢查大約25%的key是否過時,若是超過必定比例的key被過時。那麼繼續循環,直至低於這個數值。

這個按期的時間,以及數值均可以在conf文件裏面配置。

相關文章
相關標籤/搜索