瞭解Redis過時策略及實現原理

大約閱讀4分鐘,寫於 2021 0408 22:33 牀前書桌檯燈下redis

在這裏插入圖片描述

若是你使用過 redis,那你必定知道過時策略這個命令吧,若是讓你設計一個過時鍵接口,你有什麼想法?數據庫

咱們在使用 redis 時,通常會設置一個過時時間,固然也有不設置過時時間的,也就是永久不過時。緩存

當咱們設置了過時時間,redis 是如何判斷是否過時,以及根據什麼策略來進行刪除的。服務器

redis 設置過時時間:
expire key time(以秒爲單位) – 這是最經常使用的方式
setex(String key, int seconds, String value) – 字符串獨有的方式markdown

除了字符串本身獨有設置過時時間的方法外,其餘方法都須要依靠 expire 方法來設置時間併發

若是沒有設置時間,那緩存就是永不過時ide

若是設置了過時時間,以後又想讓緩存永不過時,使用 persist keymemcached


三種過時策略:性能

目錄:
atom

定時刪除

  1. 含義:在設置 key 的過時時間的同時,爲該 key 建立一個定時器,讓定時器在 key 的過時時間來臨時,對 key 進行刪除

  2. 優勢:保證內存被儘快釋放

  3. 缺點:若過時 key 不少,刪除這些 key 會佔用不少的 CPU 時間,在 CPU 時間緊張的狀況下, CPU 不能把全部的時間用來作要緊的事兒,還須要去花時間刪除這些 key,定時器的建立耗時,若爲每個設置過時時間的 key 建立一個定時器(將會有大量的定時器產生),性能影響嚴重。


懶漢式式刪除

  1. 含義:key 過時的時候不刪除,每次經過 key 獲取值的時候去檢查是否過時,若過時,則刪除,返回 null。
  2. 優勢:刪除操做只發生在經過 key 取值的時候發生,並且只刪除當前 key,因此對 CPU 時間的佔用是比較少的,並且此時的刪除是已經到了非作不可的地步(若是此時還不刪除的話,咱們就會獲取到了已通過期的 key 了)
  3. 缺點:若大量的 key 在超出超時時間後,好久一段時間內,都沒有被獲取過,那麼可能發生內存泄露(無用的垃圾佔用了大量的內存)

按期刪除

  1. 含義:每隔一段時間執行一次刪除過時 key 操做
  2. 優勢:經過限制刪除操做的時長和頻率,來減小刪除操做對 CPU 時間的佔用–處理 「定時刪除」 的缺點
  3. 缺點:在內存友好方面,不如 」定時刪除」(會形成必定的內存佔用,可是沒有懶漢式那麼佔用內存) 在 CPU 時間友好方面,不如 」懶漢式刪除」(會按期的去進行比較和刪除操做,cpu 方面不如懶漢式,可是比定時好)
  4. 難點:合理設置刪除操做的執行時長(每次刪除執行多長時間)和執行頻率(每隔多長時間作一次刪除)(這個要根據服務器運行狀況來定了),每次執行時間太長,或者執行頻率過高對 cpu 都是一種壓力。每次進行按期刪除操做執行以後,須要記錄遍歷循環到了哪一個標誌位,以便下一次按期時間來時,從上次位置開始進行循環遍歷
  5. 說明:memcached 只是用了惰性刪除,而 redis 同時使用了惰性刪除與按期刪除,這也是兩者的一個不一樣點(能夠看作是 redis 優於 memcached 的一點);對於懶漢式刪除而言,並非只有獲取 key 的時候纔會檢查 key 是否過時,在某些設置 key 的方法上也會檢查(eg.setnx key2 value2:該方法相似於 memcached 的 add 方法,若是設置的 key2 已經存在,那麼該方法返回false,什麼都不作;若是設置的 key2 不存在,那麼該方法設置緩存 key2-value2。假設調用此方法的時候,發現 redis 中已經存在了 key2,可是該key2已通過期了,若是此時不執行刪除操做的話,setnx方法將會直接返回false,也就是說此時並無從新設置 key2-value2 成功,因此對於必定要在 setnx 執行以前,對 key2 進行過時檢查)。

Redis 採用的過時策略

  • 懶漢式刪除+按期刪除

懶漢式刪除流程:

  1. 在進行 get 或 setnx 等操做時,先檢查 key 是否過時;
  2. 若過時,刪除 key,而後執行相應操做;
  3. 若沒過時,直接執行相應操做;
  4. 按期刪除流程(簡單而言,對指定個數個庫的每個庫隨機刪除小於等於指定個數個過時 key):
  5. 遍歷每一個數據庫(就是 redis.conf 中配置的 」database」 數量,默認爲 16)
  6. 檢查當前庫中的指定個數個 key(默認是每一個庫檢查 20 個 key,注意至關於該循環執行 20 次,循環體是下邊的描述)
  7. 若是當前庫中沒有一個 key 設置了過時時間,直接執行下一個庫的遍歷
  8. 隨機獲取一個設置了過時時間的 key,檢查該key是否過時,若是過時,刪除 key
  9. 判判定期刪除操做是否已經達到指定時長,若已經達到,直接退出按期刪除。

對於按期刪除,在程序中有一個全局變量 current_db 來記錄下一個將要遍歷的庫,假設有 16 個庫,咱們這一次按期刪除遍歷了 10 個,那此時的 current_db 就是 11,下一次按期刪除就從第11個庫開始遍歷,假設 current_db 等於 15 了,那麼以後遍歷就再從 0 號庫開始(此時 current_db==0)


總結

在實際中,若是咱們要本身設計過時策略, 在使用 懶漢式刪除+按期刪除 時,控制時長和頻率這個尤其關鍵,須要結合服務器性能,以及併發量等狀況進行調整,以至最佳。


Redis 有四個不一樣的命令能夠用於設置鍵的生存時間(鍵能夠存在多久)或過時時間(鍵何時會被刪除):

EXPIRE<key><ttl>命令用於將鍵 key 的生存時間設置爲 ttl 秒。

PEXPIRE<key><ttl> 命令用於將鍵 key 的生存時間設置爲 ttl 毫秒。

EXPIREAT<key><timestamp> 命令用於將鍵 key 的過時時間設置爲 timestamp 所指定的秒數時間戳。

PEXPIREAT<key><timestamp> 命令用於將鍵 key 的過時時間設置爲 timestamp 所指定的毫秒數時間戳。


原理:

雖然有多種不一樣單位和不一樣形式的設置命令,但實際上 EXPIRE、PEXPIRE、EXPIREAT 三個命令都是使用PEXPIREAT 命令來實現的:不管客戶端執行的是以上四個命令中的哪個,通過轉換以後,最終的執行效果都和執行 PEXPIREAT 命令同樣。

命令轉換

redisDb 結構的 expires 字典保存了數據庫中全部鍵的過時時間,咱們稱這個字典爲過時字典

過時字典的鍵是一個指針,這個指針指向鍵空間中的某個鍵對象(也便是某個數據庫鍵)。

過時字典的值是一個 long long 類型的整數,這個整數保存了鍵所指向的數據庫鍵的過時時間——一個毫秒精度的 UNIX 時間戳。

expires 字典

下圖展現了一個帶有過時字典的數據庫例子,在這個例子中,鍵空間保存了數據庫中的全部鍵值對,而過時字典則保存了數據庫鍵的過時時間。

爲了展現方便,圖中的鍵空間和過時字典中重複出現了兩次 alphabet 鍵對象和 book 鍵對象。在實際中,鍵空間的鍵和過時字典的鍵都指向同一個鍵對象,因此不會出現任何重複對象,也不會浪費任何空間。

一個帶有過時字典的數據庫例子

圖中的過時字典保存了兩個鍵值對:

第一個鍵值對的鍵爲 alphabet 鍵對象,值爲1385877600000,這表示數據庫鍵 alphabet 的過時時間爲1385877600000(2013年12月1日零時)。

第二個鍵值對的鍵爲 book 鍵對象,值爲 1388556000000,這表示數據庫鍵 book 的過時時間爲1388556000000(2014年1月1日零時)。當客戶端執行 PEXPIREAT 命令(或者其餘三個會轉換成 PEXPIREAT 命令的命令)爲一個數據庫鍵設置過時時間時,服務器會在數據庫的過時字典中關聯給定的數據庫鍵和過時時間。

在服務器執行如下命令以後

過時字典將新增一個鍵值對,其中鍵爲 message 鍵對象,而值則爲 1391234400000(2014年2月1日零時),如圖​:

如下是 PEXPIREAT 命令的僞代碼定義

PERSIST 命令能夠移除一個鍵的過時時間

PERSIST 命令就是 PEXPIREAT 命令的反操做:PERSIST 命令在過時字典中查找給定的鍵,並解除鍵和值(過時時間)在過時字典中的關聯。


過時鍵的斷定

經過過時字典,程序能夠用如下步驟檢查一個給定鍵是否過時:

  1. 檢查給定鍵是否存在於過時字典:若是存在,那麼取得鍵的過時時間。
  2. 檢查當前UNIX時間戳是否大於鍵的過時時間:若是是的話,那麼鍵已通過期;不然的話,鍵未過時。能夠用僞代碼來描述這一過程:

過時鍵斷定-僞代碼

相關文章
相關標籤/搜索