Redis中鍵過期功能的實現(xiàn)

前言

最近在需求開發(fā)中又用到了我們熟知的Redis字符串操作SET命令,可以設置指定key的值value及該key的生存時間(Time To Live,TTL)。相關命令的語法如下:

set key value [EX seconds] [PX milliseconds] [NX|XX]    //TTL由EX指定秒或PX指定毫秒

expire key seconds  //expire指定TTL,單位為秒
pexpire key milliseconds    //pexpire指定TTL,單位為毫秒

expireat key timestamp  //expireat指定過期時間戳,單位為秒
pexpireat key milliseconds-timestamp    //pexpireat指定過期時間戳,單位為毫秒

TTL key //key的剩余生存時間,單位是秒
PTTL key //key的剩余生存時間,單位是毫秒

PERSIST key //移除key的過期時間

這些命令用起來挺熟練,可轉念一想,Redis中鍵的自動過期是如何實現(xiàn)的呢?在翻閱資料及源碼的基礎上,本文主要從過期時間處理、自動刪除過期鍵策略等方面簡要介紹該功能的實現(xiàn)。

鍵的過期時間處理

設置過期時間

前言中提到,Redis有四個不同命令可以用于設置鍵的生存時間或過期時間。

可以通過EXPIRE或PEXPIRE命令設置該key的生存時間(Time To Live,TTL),在經(jīng)過指定秒或毫秒后,Redis服務器就會自動刪除生存時間為0的鍵key。

同時可以使用EXPIREAT或PEXPIREAT命令給鍵key設置過期時間(expire time)。

雖然命令形式多樣,但實際上EXPIRE、PEXPIRE、EXPIREAT三個命令都是使用PEXPIREAT命令來實現(xiàn)的,轉換方法很簡單,就是將過期時間換算成時間戳,并保持時間單位統(tǒng)一。

保存過期時間

redisDb結構的expires字典保存了數(shù)據(jù)庫中所有鍵的過期時間,被稱為“過期字典”。

  • 過期字典是一個指針,指向鍵空間中的某個對象(也即時某個數(shù)據(jù)庫鍵)。
  • 過期字典的值是一個long long類型的整數(shù),保存了鍵所指向的數(shù)據(jù)庫鍵的過期時間(一個毫秒精度的UNIX時間戳)。
移除過期時間

PERSIST命令可以移除一個鍵的過期時間,實際就是PEXPIREAT命令的反操作:PERSIST命令在過期字典中查找給定的鍵,并解除鍵和值(過期時間)在過期字典中的關聯(lián)。

計算并返回剩余生存時間

可以使用TTL或PTTL命令查找給定鍵key的剩余生存時間(key距離被服務器刪除還剩多少秒/毫秒),兩個命令都是通過計算鍵的過期時間和當前時間的差值實現(xiàn)的。

過期鍵的判定

Redis通過查詢過期字典的方式檢查一個給定鍵是否過期:

  1. 檢查給定鍵是否存在于過期字典:如果存在,那么取得鍵的過期時間;
  2. 檢查當前UNIX時間戳是否大于鍵的過期時間:如果是的話,name鍵已過期;否則鍵未過期。

過期鍵刪除策略

常見的三種刪除策略對比
刪除策略 實現(xiàn) 優(yōu)點 缺點
定時刪除 設置鍵的過期時間的同時,創(chuàng)建一個定時器(Timer),讓定時器在鍵的過期時間來臨時,立即執(zhí)行對鍵的刪除操作。 內存占用率低,通過使用定時器,可以保證過期鍵會盡可能快地被刪除,并釋放過期鍵所占的內存。 占用較多cpu時間,影響服務器的響應時間和吞吐量。
惰性刪除 放任鍵過期不管,但是每次獲取鍵時,都檢查鍵是否已過期,如果過期則刪除該鍵;否則返回該鍵。 cpu占用率低,只會在取出鍵時才進行過期檢查,可以保證刪除的目標僅限于當前的鍵,不會在其它過期鍵上花費任何cpu時間。 浪費內存,有內存泄漏的風險。
定期刪除 每隔一段時間就對數(shù)據(jù)庫做一次過期鍵的刪除。但每次要刪除多少過期鍵、要檢查多少個db,則由算法決定。 是前兩種策略的整合和折中,減少了內存和cpu的無謂占用。 難以確定刪除操作執(zhí)行的時長和頻率。

Redis的過期鍵刪除策略

Redis服務器實際使用的是惰性刪除和定期刪除兩種策略:通過配合使用這兩種策略,服務器可以很好地在合理使用cpu和避免浪費內存空間之間取得平衡。

惰性刪除策略的實現(xiàn)

過期鍵的惰性刪除策略由db.c/expireIfNeeded函數(shù)實現(xiàn),所有讀寫數(shù)據(jù)庫的Redis命令在執(zhí)行之前都會調用expireIfNeeded函數(shù)對輸入鍵進行檢查:

  • 如果輸入鍵已經(jīng)過期,那么expireIfNeeded函數(shù)將輸入鍵從數(shù)據(jù)庫中刪除。
  • 如果輸入鍵未過期,那么expireIfNeeded函數(shù)不做動作。

expireIfNeeded函數(shù)就像一個過濾器,它可以在命令真正執(zhí)行之前,過濾掉過期的輸入鍵,從而避免命令接觸到過期鍵。另外,因為每個被訪問的鍵都可能因為過期而被expireIfNeeded函數(shù)刪除,所以每個命令的實現(xiàn)函數(shù)都必須能同時處理鍵存在和不存在的情況:

  • 當鍵存在時,命令按照鍵存在的情況執(zhí)行。
  • 當鍵不存在或者鍵因為過期而被expireIfNeeded函數(shù)刪除時,命令按照鍵不存在的情況執(zhí)行。
定期刪除策略的實現(xiàn)

過期鍵的定期刪除策略由redis.c/activeExpireCycle函數(shù)實現(xiàn),每當Redis的服務器周期性操作redis.c/serverCron函數(shù)執(zhí)行時,activeExpireCycle函數(shù)就會被調用,它在規(guī)定的時間內,分多次遍歷服務器中的各個數(shù)據(jù)庫,從數(shù)據(jù)庫的expires字典中隨機檢查一部分鍵的過期時間,并刪除其中的過期鍵。

activeExpireCycle函數(shù)的工作模式可以總結如下:

  • 函數(shù)每次運行時,都從一定數(shù)量的數(shù)據(jù)庫(取min(默認16,實際數(shù)量))中取出一定數(shù)量的隨機鍵(默認20)進行檢查,并刪除其中的過期鍵。
  • 全局變量current_db會記錄當前activeExpireCycle函數(shù)檢查的進度,并在下一次activeExpireCycle函數(shù)調用時,接著上一次的進度進行處理。
  • 隨著activeExpireCycle函數(shù)的不斷執(zhí)行,服務器中的所有db都會被檢查一遍,這時函數(shù)將current_db變量重置為0,然后進行新一輪的定期刪除。

小結

本文對Redis中鍵過期功能的實現(xiàn)做了一個簡要介紹,相信讀者看完之后會對大致的實現(xiàn)方案有所了解,但更多細節(jié)推薦閱讀《Redis涉及與實現(xiàn)》,當然想自己去研究源碼更好啦。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 1.Redis特性 1)速度快:數(shù)據(jù)存放在內存上、基于C語言實現(xiàn)、單線程架構預防多線程競爭問題;2)基于鍵值對的數(shù)...
    Sponge1128閱讀 749評論 0 1
  • 制定Redis過期策略,是整個Redis緩存策略的關鍵之一,因為內存來說,公司不可能無限大,所以就要對key進行一...
    JackFrost_fuzhu閱讀 4,926評論 1 10
  • 9.1 服務器中的數(shù)據(jù)庫 Redis服務器將所有的數(shù)據(jù)庫都保存在服務器狀態(tài)redis.h/redisServer結...
    豬大金閱讀 408評論 0 0
  • 這節(jié)課告訴我,想要內心愉快的堅持下去,想明白很重要,才能真正的學以致用,也能做好家人的榜樣,尤其是孩子的榜樣。 學...
    平和生活閱讀 602評論 0 1
  • 面具外 你微笑著 誰也不知道 你的失落 面具內 你傷心著 誰也不知道 你的痛苦 你不露聲色 怕被看穿 你無動于衷 ...
    Keya的意識空間閱讀 325評論 5 5

友情鏈接更多精彩內容