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:

dict是用來存儲正常數據的,比如我們執行了set key “hahaha”,這個數據就存儲在dict中。

expires使用來存儲關聯了過期時間的key的,比如我們在上面的基礎之上有執行的expire key 1,這個時候就會在expires中添加一條記錄。

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

從expires中查找key的過期時間,如果不存在說明對應key沒有設置過期時間,直接返回。

如果是slave機器,則直接返回,因為Redis為了保證數據一致性且實現簡單,將緩存失效的主動權交給Master機器,slave機器沒有權限將key失效。

如果當前是Master機器,且key過期,則master會做兩件重要的事情:1)將刪除命令寫入AOF文件。2)通知Slave當前key失效,可以刪除了。

master從本地的字典中將key對于的值刪除。

主動失效機制

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

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

一類是IO事件,這類事件是從底層的多路復用器分離出來的。

一類是定時事件,這類事件主要用來事件對某個任務的定時執行。

看起來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知識請關注redis入門教程欄目。

? 版權聲明
THE END
喜歡就支持一下吧
點贊8 分享