Redis緩存失效機制

Redis緩存失效的故事要從EXPIRE這個命令提及,EXPIRE容許用戶爲某個key指定超時時間,當超過這個時間以後key對應的值會被清除,這篇文章主要在分析Redis源碼的基礎上站在Redis設計者的角度去思考Redis緩存失效的相關問題。redis

 

Redis緩存失效機制

Redis緩存失效機制是爲應對緩存應用的一種很常見的場景而設計的,講個場景:數據庫

咱們爲了減輕後端數據庫的壓力,很開心的藉助Redis服務把變化頻率不是很高的數據從DB load出來放入了緩存,所以以後的一段時間內咱們均可以直接從緩存上拿數據,然而咱們又但願一段時間以後,咱們再從新的從DB load出當前的數據放入緩存,這個事情怎麼作呢?

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

EXPIRE key 30

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

 

延遲失效機制

延遲失效機制即當客戶端請求操做某個key的時候,Redis會對客戶端請求操做的key進行有效期檢查,若是key過時才進行相應的處理,延遲失效機制也叫消極失效機制。咱們看看t_string組件下面對get請求處理的服務端端執行堆棧:網絡

getCommand 
     -> getGenericCommand 
            -> lookupKeyReadOrReply 
                   -> lookupKeyRead 
                         -> expireIfNeeded

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

typedef struct redisDb {
    dict *dict;                 /* The keyspace for this DB */
    dict *expires;              /* Timeout of keys with a timeout set */
    dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP) */
    dict *ready_keys;           /* Blocked keys that received a PUSH */
    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */
    int id;
    long long avg_ttl;          /* Average TTL, just for stats */
} redisDb;

上面是Redis中定義的一個結構體,dict是一個Redis實現的一個字典,也就是每一個DB會包括上面的五個字段,咱們這裏只關心兩個字典,一個是dict,一個是expires:工具

  1. dict是用來存儲正常數據的,好比咱們執行了set key "hahaha",這個數據就存儲在dict中。
  2. expires使用來存儲關聯了過時時間的key的,好比咱們在上面的基礎之上有執行的expire key 1,這個時候就會在expires中添加一條記錄。

回過頭來看看expireIfNeeded的流程,大體以下:oop

  1. 從expires中查找key的過時時間,若是不存在說明對應key沒有設置過時時間,直接返回。
  2. 若是是slave機器,則直接返回,由於Redis爲了保證數據一致性且實現簡單,將緩存失效的主動權交給Master機器,slave機器沒有權限將key失效。
  3. 若是當前是Master機器,且key過時,則master會作兩件重要的事情:1)將刪除命令寫入AOF文件。2)通知Slave當前key失效,能夠刪除了。
  4. master從本地的字典中將key對於的值刪除。

 

主動失效機制

主動失效機制也叫積極失效機制,即服務端定時的去檢查失效的緩存,若是失效則進行相應的操做。this

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

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

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

爲何講到Redis的單線程模型,由於Redis的主動失效機制邏輯是被當作一個定時任務來由主線程執行的,相關代碼以下:

if(aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
        redisPanic("Can't create the serverCron time event.");
        exit(1);
    }

serverCron就是這個定時任務的函數指針,adCreateTimeEvent將serverCron任務註冊到EventLoop上面,並設置初始的執行時間是1毫秒以後。接下來,咱們想知道的東西都在serverCron裏面了。serverCron作的事情有點多,咱們只關心和本篇內容相關的部分,也就是緩存失效是怎麼實現的,我認爲看代碼作什麼事情,調用堆棧仍是比較直觀的:

aeProcessEvents
    ->processTimeEvents
        ->serverCron 
             -> databasesCron 
                   -> activeExpireCycle 
                           -> activeExpireCycleTryExpire

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

 

遺留問題

Redis對緩存失效的處理機制大概分爲兩種,一種是客戶端訪問key的時候消極的處理,一種是主線程按期的積極地去執行緩存失效清理邏輯,上面文章對於一些細節尚未展開介紹,可是對於Redis緩存失效實現機制這個話題,本文留下幾個問題:

  1. Redis緩存失效邏輯爲何只有master才能操做?
  2. 上面提到若是客戶端訪問的是slave,slave並不會清理失效緩存,那麼此次客戶端豈不是獲取了失效的緩存?
  3. 上面介紹的兩種緩存失效機制各有什麼優缺點?Redis設計者爲何這麼設計?
  4. 服務端對客戶端的請求處理是單線程的,單線程又要去處理失效的緩存,是否是會影響Redis自己的服務能力?

 

參考文獻

《Redis源碼》

相關文章
相關標籤/搜索