Redis 快取失效機制

Float_Lu發表於2016-06-09

Redis快取失效的故事要從EXPIRE這個命令說起,EXPIRE允許使用者為某個key指定超時時間,當超過這個時間之後key對應的值會被清除,這篇文章主要在分析Redis原始碼的基礎上站在Redis設計者的角度去思考Redis快取失效的相關問題。

Redis快取失效機制

Redis快取失效機制是為應對快取應用的一種很常見的場景而設計的,講個場景:

我們為了減輕後端資料庫的壓力,很開心的藉助Redis服務把變化頻率不是很高的資料從DB load出來放入了快取,因此之後的一段時間內我們都可以直接從快取上拿資料,然而我們又希望一段時間之後,我們再重新的從DB load出當前的資料放入快取,這個事情怎麼做呢?

問題提出來了,這個問題怎麼解決呢?好吧,我們對於手頭的語言工具很熟悉,堅信可以很快的寫出這麼一段邏輯:我們記錄上次從db load資料的時間,然後每次響應服務的時候都去判斷時間是不是過期了,要不要從db重新load了……。當然這種方法也是可以的,然而當我們查閱Redis command document的時候,發現我們做了本來不需要做的事情,Redis本身提供這種機制,我們只要藉助EXPIRE命令就可以輕鬆的搞定這件事情:

上面的命令即為key設定30秒的過期時間,超過這個時間,我們應該就訪問不到這個值了,到此為止我們大概明白了什麼是快取失效機制以及快取失效機制的一些應用場景,接下來我們繼續深入探究這個問題,Redis快取失效機制是如何實現的呢?

延遲失效機制

延遲失效機制即當客戶端請求操作某個key的時候,Redis會對客戶端請求操作的key進行有效期檢查,如果key過期才進行相應的處理,延遲失效機制也叫消極失效機制。我們看看t_string元件下面對get請求處理的服務端端執行堆疊:

關鍵的地方是expireIfNeed,Redis對key的get操作之前會判斷key關聯的值是否失效,這裡先插入一個小插曲,我們看看Redis中實際儲存值的地方是什麼樣子的:

上面是Redis中定義的一個結構體,dict是一個Redis實現的一個字典,也就是每個DB會包括上面的五個欄位,我們這裡只關心兩個字典,一個是dict,一個是expires:

  1. dict是用來儲存正常資料的,比如我們執行了set key “hahaha”,這個資料就儲存在dict中。
  2. expires使用來儲存關聯了過期時間的key的,比如我們在上面的基礎之上有執行的expire key 1,這個時候就會在expires中新增一條記錄。

回過頭來看看expireIfNeeded的流程,大致如下:

  1. 從expires中查詢key的過期時間,如果不存在說明對應key沒有設定過期時間,直接返回。
  2. 如果是slave機器,則直接返回,因為Redis為了保證資料一致性且實現簡單,將快取失效的主動權交給Master機器,slave機器沒有許可權將key失效。
  3. 如果當前是Master機器,且key過期,則master會做兩件重要的事情:1)將刪除命令寫入AOF檔案。2)通知Slave當前key失效,可以刪除了。
  4. master從本地的字典中將key對於的值刪除。

主動失效機制

主動失效機制也叫積極失效機制,即服務端定時的去檢查失效的快取,如果失效則進行相應的操作。

我們都知道Redis是單執行緒的,基於事件驅動的,Redis中有個EventLoop,EventLoop負責對兩類事件進行處理:

  1. 一類是IO事件,這類事件是從底層的多路複用器分離出來的。
  2. 一類是定時事件,這類事件主要用來事件對某個任務的定時執行。

看起來Redis的EventLoop和Netty以及JavaScript的EventLoop功能設計的大概類似,一方面對網路I/O事件處理,一方面還可以做一些小任務。

為什麼講到Redis的單執行緒模型,因為Redis的主動失效機制邏輯是被當做一個定時任務來由主執行緒執行的,相關程式碼如下:

serverCron就是這個定時任務的函式指標,adCreateTimeEvent將serverCron任務註冊到EventLoop上面,並設定初始的執行時間是1毫秒之後。接下來,我們想知道的東西都在serverCron裡面了。serverCron做的事情有點多,我們只關心和本篇內容相關的部分,也就是快取失效是怎麼實現的,我認為看程式碼做什麼事情,呼叫堆疊還是比較直觀的:

EventLoop通過對定時任務的處理,觸發對serverCron邏輯的執行,最終之執行key過期處理的邏輯,值得一提的是,activeExpireCycle邏輯只能由master來做。

遺留問題

Redis對快取失效的處理機制大概分為兩種,一種是客戶端訪問key的時候消極的處理,一種是主執行緒定期的積極地去執行快取失效清理邏輯,上面文章對於一些細節還沒有展開介紹,但是對於Redis快取失效實現機制這個話題,本文留下幾個問題:

  1. Redis快取失效邏輯為什麼只有master才能操作?
  2. 上面提到如果客戶端訪問的是slave,slave並不會清理失效快取,那麼這次客戶端豈不是獲取了失效的快取?
  3. 上面介紹的兩種快取失效機制各有什麼優缺點?Redis設計者為什麼這麼設計?
  4. 服務端對客戶端的請求處理是單執行緒的,單執行緒又要去處理失效的快取,是不是會影響Redis本身的服務能力?

參考文獻

《Redis原始碼》

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

任選一種支付方式

Redis 快取失效機制 Redis 快取失效機制

相關文章