Redis中過期鍵刪除的三種方法
Redis中可以設(shè)置鍵的過期時間,并且通過取出過期字典(expires dict)中鍵的過期時間和當(dāng)前時間比較來判斷是否過期。
那么一個過期的鍵是怎么被刪除的呢?
定時刪除
定時刪除:在設(shè)置鍵的過期時間時,創(chuàng)建一個定時器(timer),讓定時器在鍵的過期時間來臨時,立即執(zhí)行對鍵的刪除操作。
這是對內(nèi)存最友好的,因為他通過定時器保證了過期即會刪除,并釋放過期鍵占用的內(nèi)存。但是這是對CPU最不友好的,在過期鍵比較多的情況下,刪除大量過期鍵會占用相當(dāng)一部分CPU時間,會對服務(wù)器的的響應(yīng)時間和吞吐量造成影響。
如果服務(wù)器當(dāng)前不缺少內(nèi)存,那么服務(wù)器應(yīng)該優(yōu)先將CPU時間用在處理客戶端的命令請求上面,而不是用在刪除過期鍵上面。
而且,創(chuàng)建一個定時器需要用到Redis服務(wù)器中的時間事件,而其實現(xiàn)方式是無序鏈表,所以查找一個事件的時間復(fù)雜度為O(N),并不能高效地處理大量時間事件。
惰性刪除
惰性刪除:放任鍵過期不管,但是每次從鍵空間中獲取鍵時,都檢查取得的鍵是否過期,如果過期的話,就刪除該鍵;如果沒有過期,就返回該鍵。
惰性刪除策略對CPU時間來說是最友好的:程序只會在取出鍵時才對鍵進行過期檢查。但它對內(nèi)存是最不友好的,如果一個鍵已經(jīng)過期,那么只要這個過期鍵不被訪問,它所占用的內(nèi)存就不會釋放。(甚至可以將這種情況看作是一種內(nèi)存泄漏,這對于運行狀態(tài)非常依賴于內(nèi)存的Redis服務(wù)器來說,肯定不是一個好消息)
定期刪除
定期刪除:每隔一段時間,程序就對數(shù)據(jù)庫進行一次檢查,刪除里面的過期鍵。至于要刪除多少過期鍵,以及要檢查多少個數(shù)據(jù)庫,則由算法決定。
定期刪除策略是前兩種策略的一種整合和折中,它的難點是確定刪除操作執(zhí)行的時長和頻率:
- 如果刪除操作執(zhí)行得太頻繁,或者執(zhí)行的時間太長,定期刪除策略就會退化成定時
刪除策略,以至于將CPU時間過多地消耗在刪除過期鍵上面。 - 如果刪除操作執(zhí)行得太少,或者執(zhí)行的時間太短,定期刪除策略又會和惰性刪除策
略一樣,出現(xiàn)浪費內(nèi)存的情況
因此,如果采用定期刪除策略的話,服務(wù)器必須根據(jù)情況合理地設(shè)置刪除操作的執(zhí)行時長和執(zhí)行頻率。
Redis的實現(xiàn)
Redis 選擇「惰性刪除+定期刪除」這兩種策略配和使用
惰性刪除
惰性刪除策略由db.c/expireIfNeeded函數(shù)實現(xiàn),所有讀寫數(shù)據(jù)庫的Redis命令在執(zhí)行之前都會調(diào)用expireIfNeeded函數(shù)對輸入鍵進行檢查:如果輸人鍵已經(jīng)過期,那么將輸人鍵從數(shù)據(jù)庫中刪除;如果輸人鍵未過期,則不做動作。
Redis 的惰性刪除策略由db.c文件中的 expireIfNeeded 函數(shù)實現(xiàn),代碼如下:
int expireIfNeeded(redisDb *db, robj *key) { // 判斷 key 是否過期 if (!keyIsExpired(db,key)) return 0; // 刪除過期鍵 // 如果 server.lazyfree_lazy_expire 為 1 表示異步刪除,反之同步刪除; return server.lazyfree_lazy_expire ? dbAsyncDelete(db,key) : dbSyncDelete(db,key); }
Redis 在訪問或者修改 key 之前,都會調(diào)用 expireIfNeeded 函數(shù)對其進行檢查,檢查 key 是否過期:
- 如果過期,則刪除該 key,至于選擇異步刪除,還是選擇同步刪除,根據(jù)
lazyfree_lazy_expire
參數(shù)配置決定(Redis 4.0版本開始提供參數(shù)),然后返回 null 客戶端 - 如果沒有過期,正常處理指令
定期刪除
在 Redis 中,默認(rèn)每秒進行 10 次過期檢查一次數(shù)據(jù)庫,此配置可通過 Redis 的配置文件 redis.conf 進行配置(hz
),它的默認(rèn)值是10。
過期鍵的定期刪除策略由redis.c/activeExpireCycle函數(shù)實現(xiàn),每當(dāng)Redis的服務(wù)器周期性操作redis.c/serverCron函數(shù)執(zhí)行時,activeExpireCycle函數(shù)就會被調(diào)用,它在規(guī)定的時間內(nèi),分多次遍歷服務(wù)器中的各個數(shù)據(jù)庫,從數(shù)據(jù)庫的過期字典(expires dict)中隨機檢查一部分鍵的過期時間,并刪除其中的過期鍵。
過程用偽代碼表示為:
#代碼來自《Redis設(shè)計與實現(xiàn)》,版本是Redis 2.9。跟新版redis應(yīng)該有所偏差 #默認(rèn)每次檢查的數(shù)據(jù)庫數(shù)量 DEFAULT_ DB_ NUMBERS = 16 #默認(rèn)每個數(shù)據(jù)庫檢查的鍵數(shù)量 DEFAULT KEY_ NUMBERS = 20 #全局變量,記錄檢查進度. current_ db = 0 def activeExpireCycle() : #遍歷各個數(shù)據(jù)庫 for i in range (db_numbers) : #已經(jīng)遍歷一輪,將current_ db重置為0,開始新的一輪遍歷 if current_db == server.dbnum: current_db = 0 #獲取當(dāng)前要處理的數(shù)據(jù)庫 redisDb = server.db[current__db] #將數(shù)據(jù)庫索引增1,指向下一個要處理的數(shù)據(jù)庫 current_db += 1 #檢查數(shù)據(jù)庫鍵 for j in range (DEFAULT_KEY_NUMBERS) : #如果數(shù)據(jù)庫中沒有一個鍵帶有過期時間,那么跳過這個數(shù)據(jù)庫 if redisDb.expires.size() == 0: break #隨機獲取一個帶有過期時間的鍵 key_with_ttl = redisDb.expires.get_random_key () #檢查鍵是否過期,如果過期就刪除它. if is_expired(key_with_ttl) : delete_key(key_with_ttl) #已達到時間上限,停止處理 if reach_time_limit() : return
總的來說就是,定期刪除會在規(guī)定時間(時間不超過上限,25ms)內(nèi)依次遍歷所有數(shù)據(jù)庫,對于每個數(shù)據(jù)庫會依次隨機獲取20
個鍵并刪除過期的鍵,如果過期鍵超過25%會繼續(xù)隨機獲取20個鍵。
以上就是Redis中過期鍵刪除的三種方法的詳細(xì)內(nèi)容,更多關(guān)于Redis過期鍵刪除的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Redis高并發(fā)情況下并發(fā)扣減庫存項目實戰(zhàn)
本文主要介紹了Redis高并發(fā)情況下并發(fā)扣減庫存項目實戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04Redis Key的數(shù)量上限及優(yōu)化策略分享
Redis 作為高性能的鍵值存儲數(shù)據(jù)庫,廣泛應(yīng)用于緩存、會話存儲、排行榜等場景,但在實際使用中,開發(fā)者常常會關(guān)心一個問題:Redis 的 Key 數(shù)量是否有上限?本文將從 Redis Key 的理論上限 出發(fā),深入探討 Redis Key 的管理策略,需要的朋友可以參考下2025-03-03關(guān)于redis Key淘汰策略的實現(xiàn)方法
下面小編就為大家?guī)硪黄P(guān)于redis Key淘汰策略的實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-03-03