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