redis-刪除策略
Redis過(guò)期刪除策略
作者:w08e
?? NOTE
redis熱門問(wèn)題 沖沖沖
問(wèn)題思路:

回答話術(shù)
在redis中有兩種方式設(shè)置key的過(guò)期時(shí)間,一種是直接設(shè)置到期時(shí)間expireAt,一種是設(shè)置key的有效期expire
這兩種方式最后都會(huì)轉(zhuǎn)換為時(shí)間戳與key同時(shí)存到一個(gè)到期字典里,然后 Redis 會(huì)根據(jù) Key 在到期字典中的到期時(shí)間,通過(guò)主動(dòng)和被動(dòng)兩種方式清理到期的 Key。
主動(dòng)刪除: Redis 會(huì)每秒主動(dòng)掃描 10 次到期字典,隨機(jī)抽取 20 個(gè) Key 并刪除其中已經(jīng)到期的部分。然后,如果這次抽樣中到期鍵的 Key 的比例超過(guò) 25%,就會(huì)繼續(xù)抽樣,直到不滿足條件或超時(shí)為止。
被動(dòng)刪除:每次訪問(wèn) Key 鍵時(shí),Redis 會(huì)檢查 Key 是否已到期,如果是就將其刪除并返回空值。不過(guò)如果僅靠被動(dòng)刪除是不夠的,因?yàn)槿绻?Key 的訪問(wèn)頻率不高,可能會(huì)導(dǎo)致一些數(shù)據(jù)一直不能被刪除,內(nèi)存也無(wú)法得到釋放,因此所以還需要定期的主動(dòng)刪除。
以上兩種刪除機(jī)制互相配合,基本能保證 Redis 中到期鍵的數(shù)量不會(huì)超過(guò)總數(shù)據(jù)量的 25%。
另外,Redis 在持久化的時(shí)候也會(huì)針對(duì)到期的 Key 做額外的處理。Redis 在 AOF 的時(shí)候,如果 Key 過(guò)期了,則會(huì)向文件追加一條 DEL 指令,而如果是在 AOF 重寫和 RDB 的時(shí)候,則檢查并直接忽略掉過(guò)期的 Key。
最后是集群,在集群里面,當(dāng)主節(jié)點(diǎn)發(fā)現(xiàn) Key 到期時(shí),會(huì)向所有從節(jié)點(diǎn)發(fā)送 DEL 命令,但是當(dāng)從節(jié)點(diǎn)發(fā)現(xiàn)鍵到期時(shí),只會(huì)將其標(biāo)記為已刪除,直到收到主節(jié)點(diǎn)的刪除指令才會(huì)真正刪除,以確保數(shù)據(jù)一致性。
理論詳解:
1. 相關(guān)指令:
在 Redis 中,你可以使用以下四種命令為 Key 設(shè)置到期時(shí)間:
EXPIRE:以秒為單位,設(shè)置 Key 的有效時(shí)間。PEXPIRE:以毫秒為單位,設(shè)置 Key 的有效時(shí)間。EXPIREAT:以秒為單位,設(shè)置 Key 的到期時(shí)間戳。PEXPIREAT:以毫秒為單位,設(shè)置 Key 的到期時(shí)間戳。
其中,前兩者指定的是 Key 的有效時(shí)長(zhǎng),而后兩者指定的是 Key 到期時(shí)間點(diǎn)。
不過(guò),在 Redis 底層實(shí)現(xiàn)中,四種命令最終都會(huì)變?yōu)?Key 到期時(shí)間點(diǎn)對(duì)應(yīng)的時(shí)間戳,并被記錄在一個(gè)到期字典中(哈希表)。
2. 刪除策略:
按官方文檔的說(shuō)法,Redis 的過(guò)期刪除有兩種方式:
-
主動(dòng)刪除:每 10 秒掃描一次數(shù)據(jù)庫(kù),隨機(jī)抽 20 個(gè) key,并刪除其中到期的 key。如果到期 Key 占比超過(guò) 25%,那么繼續(xù)抽樣,直到不滿足條件或超時(shí)為止;
-
被動(dòng)刪除:訪問(wèn) Key 時(shí)檢查到期時(shí)間,如果已經(jīng)到期就刪除;
?? NOTE
官方文檔對(duì)此進(jìn)行了解釋:Redis 官網(wǎng) -- EXPIRE 指令介紹
2.1. 定時(shí)刪除
這里我們順便提一下一個(gè) Redis 沒(méi)有使用,而非常簡(jiǎn)單粗暴的思路,那就是定時(shí)刪除。
簡(jiǎn)單的來(lái)說(shuō),就是在設(shè)置 Key 的到期時(shí)間時(shí),一并設(shè)置一個(gè)定時(shí)事件,等到事件觸發(fā)時(shí)刪除 key。
- 優(yōu)點(diǎn): 可以及時(shí)釋放資源,確保過(guò)期鍵能夠被及時(shí)刪除。
- 缺點(diǎn): 頻繁的刪除操作可能會(huì)占用大量的 CPU 時(shí)間。
總的來(lái)說(shuō),這個(gè)策略對(duì)內(nèi)存優(yōu)化而對(duì) CPU 不友好,在 CPU 緊張而內(nèi)存寬裕的場(chǎng)景中,它會(huì)將更多的 CPU 資源花費(fèi)到?jīng)]那么緊要的刪除到期 Key 操作上。
綜合上述考量, Redis 并沒(méi)有使用這種方式。
2.2. 惰性刪除
惰性刪除就是 Redis 提到的被動(dòng)刪除。
被動(dòng)刪除不會(huì)主動(dòng)的刪除到期的 key,而是當(dāng)訪問(wèn) Key 時(shí)再檢查是否到期,如果到期了再將其刪除。
它的優(yōu)缺點(diǎn)與定時(shí)刪除剛好相反:
- 優(yōu)點(diǎn): 只在取出鍵時(shí)進(jìn)行檢查,避免了頻繁的刪除操作。
- 缺點(diǎn): 可能會(huì)導(dǎo)致內(nèi)存積壓?jiǎn)栴}。
惰性刪除對(duì) CPU 最友好,但是對(duì)內(nèi)存就不友好了。尤其是當(dāng)你需要在 Redis 中存放大量具備到期時(shí)間且不需要頻繁訪問(wèn)的數(shù)據(jù)時(shí),會(huì)造成內(nèi)存積壓。
2.3. 定期刪除
定期刪除就是 Redis 提到的主動(dòng)刪除。
具體來(lái)說(shuō),整個(gè)過(guò)程如下:
- Redis 定時(shí)(取決于
hz配置,默認(rèn)為 10,即每秒 10 次)依次遍歷 16 個(gè)數(shù)據(jù)庫(kù):- 如果此時(shí)到期字典可用率較低,考慮到哈希沖突嚴(yán)重時(shí)鏈表可能很長(zhǎng),遍歷需要額外的時(shí)間成本,那么將會(huì)直接跳過(guò)該數(shù)據(jù)庫(kù),等到下一次循環(huán)其重哈希以后再進(jìn)行處理。
- 如果字典可用率處于正常水平,那么就依次從數(shù)據(jù)庫(kù)的到期字典中對(duì) Key 進(jìn)行抽樣(20 個(gè)),并刪除已超時(shí)的 Key;
- 如果此次抽樣中,到期 Key 的占比高于一定閾值(25%),則會(huì)再進(jìn)行一次抽樣刪除,直到到期 Key 占比沒(méi)那么高。或者本次任務(wù)執(zhí)行超時(shí)為止;
- 如果此次執(zhí)行超時(shí)了,那么將會(huì)記錄當(dāng)前處理的數(shù)據(jù)庫(kù)下標(biāo),然后下次進(jìn)行抽樣時(shí)就直接從當(dāng)前數(shù)據(jù)庫(kù)開(kāi)始執(zhí)行,如此反復(fù)。
相對(duì)于定時(shí)刪除和惰性刪除,定期刪除在內(nèi)存和 CPU 消耗中取得了一個(gè)比較好的平衡。
另外,使用抽樣避免全量操作的思想在 Redis 中挺常見(jiàn)的,比如內(nèi)存淘汰策略中的近似 LRU 和 LFU,具體可以參見(jiàn)下
2.3.1. 為什么要抽樣而不是全量檢查?
每次大批量的篩選并刪除 Key 是十分消耗性能的,并且長(zhǎng)時(shí)間的阻塞 Redis 的主線程還會(huì)降低吞吐量。為了避免上述的問(wèn)題,Redis 需要通過(guò)控制篩選范圍降低 Key 的數(shù)量,從而來(lái)提高操作性能并降低阻塞的時(shí)間。
當(dāng)然,即便如此,如果你為大量的 Key 設(shè)置了接近的超時(shí)時(shí)間,那么當(dāng)它們同時(shí)失效 —— 我們一般稱呼這種常見(jiàn)為緩存雪崩 —— 時(shí),由于每次抽樣中到期 Key 占比都會(huì)高于 25% 的概率極高,那么 Redis 依然不得不花費(fèi)更多的時(shí)間來(lái)刪除大量的 Key。雖然有一個(gè)整體超時(shí)時(shí)間來(lái)兜底,但是為了提高效率,還是最好通過(guò)設(shè)置不同的超時(shí)時(shí)間之類的操作盡可能避免這種情況。
關(guān)于這部分內(nèi)容,可以參見(jiàn):? 如何解決緩存雪崩?
2.3.2. 如何控制定期刪除的觸發(fā)頻率?
一般來(lái)說(shuō),我們可以通過(guò)配置文件中的 hz 參數(shù)來(lái)指定定期刪除任務(wù)的觸發(fā)頻率,它默認(rèn)為 10,即每 1s 執(zhí)行 10 次,最大可以調(diào)成 500。
需要注意的是,這個(gè)參數(shù)實(shí)際上不止用于控制定期刪除的執(zhí)行頻率,redis 中幾乎所有定期執(zhí)行的后臺(tái)任務(wù)都通過(guò)該值來(lái)設(shè)置 —— 比如關(guān)閉超時(shí)的客戶端連接,或者更新統(tǒng)計(jì)信息之類的 —— 因此,調(diào)大這個(gè)值會(huì)導(dǎo)致 Redis 服務(wù)占用更多的 CPU 資源。不過(guò),如果你的 Redis 服務(wù)會(huì)有大量的 ttl 極短的 Key,那么你可以適量的調(diào)大 hz參數(shù)來(lái)及時(shí)清理掉這些朝生夕死的 Key。
總而言之,這是又是一個(gè) CPU 和內(nèi)存占用的權(quán)衡問(wèn)題,不過(guò) Redis 也提供了 dynamic-hz 的配置,當(dāng)你設(shè)置為 yes 的時(shí)候,Redis 會(huì)根據(jù)情況適當(dāng)?shù)母鶕?jù) hz 調(diào)整實(shí)際的觸發(fā)頻率,這個(gè)配置默認(rèn)都是開(kāi)啟的。
這里我們直接放上配置文件,你可以結(jié)合注釋來(lái)感受一下:
# Redis調(diào)用一個(gè)內(nèi)部函數(shù)來(lái)執(zhí)行許多后臺(tái)任務(wù),比如
# 關(guān)閉超時(shí)的客戶端連接,清理從未被請(qǐng)求的過(guò)期鍵等等。
#
# 并非所有任務(wù)的執(zhí)行頻率都相同,但Redis根據(jù)指定的“hz”值檢查要執(zhí)行的任務(wù)。
#
# 默認(rèn)情況下,“hz”設(shè)置為10。增加這個(gè)值會(huì)在Redis空閑時(shí)使用更多CPU資源,
# 但同時(shí)會(huì)使得Redis在有許多鍵同時(shí)過(guò)期時(shí)響應(yīng)更快,并且超時(shí)處理可能會(huì)更精確。
#
# 可選值范圍在1到500之間,但通常超過(guò)100不是一個(gè)好主意。大多數(shù)用戶應(yīng)該使用默認(rèn)值10,
# 在需要非常低延遲的環(huán)境中可以將其提高到100。
hz 10
# 通常情況下,擁有與連接客戶端數(shù)量成比例的HZ值是有用的。
# 例如,這對(duì)于避免在每次后臺(tái)任務(wù)調(diào)用中處理過(guò)多客戶端以避免延遲峰值很有用。
#
# 由于默認(rèn)情況下HZ值保守地設(shè)置為10,Redis提供并默認(rèn)啟用了使用自適應(yīng)HZ值的能力,
# 當(dāng)有許多連接的客戶端時(shí)會(huì)臨時(shí)提高HZ值。
#
# 啟用動(dòng)態(tài)HZ時(shí),實(shí)際配置的HZ將作為基線使用,但一旦連接更多客戶端,
# 將根據(jù)需要使用配置的HZ值的倍數(shù)。這樣,空閑實(shí)例將使用非常少的CPU時(shí)間,而繁忙實(shí)例將更具響應(yīng)性。
dynamic-hz yes
3. 持久化:
Redis 使用 AOF 與 RBD 兩種方式來(lái)持久化內(nèi)存中數(shù)據(jù),這個(gè)過(guò)程同樣需要考慮如何處理過(guò)期的 key:
- AOF:當(dāng) Key 因?yàn)榈狡诙粍h除時(shí),將會(huì)向 AOF 追加一條
**DEL**命令。如果在這個(gè)過(guò)程中進(jìn)行了 AOF 重寫,那么重寫后的 AOF 文件中則將直接忽略掉這個(gè)過(guò)期的 Key。 - RDB:與 AOF 重寫類似,在創(chuàng)建 RDB 的時(shí)候,過(guò)期的 Key 會(huì)被直接忽略。
具體關(guān)于 AOF 與 RDB 相關(guān)內(nèi)容,可以參見(jiàn)文章:? Redis 宕機(jī)數(shù)據(jù)會(huì)丟失么?
4. 集群同步:
當(dāng)集群中的實(shí)例發(fā)現(xiàn) Key 到期后,實(shí)例會(huì)根據(jù)它自己是主節(jié)點(diǎn)還是從節(jié)點(diǎn)而采取不同的行為:
- 如果是主節(jié)點(diǎn),它會(huì)在刪除這個(gè)過(guò)期 Key 后向所有從節(jié)點(diǎn)發(fā)送一個(gè)
DEL命令。 - 如果是從節(jié)點(diǎn),那么它將會(huì)將這個(gè) Key 標(biāo)記為到期,但并不會(huì)真正的刪除。只有當(dāng)接到從主節(jié)點(diǎn)發(fā)來(lái)的
DEL命令之后,才會(huì)真正的將過(guò)期鍵刪除掉。
從節(jié)點(diǎn)不會(huì)主動(dòng)刪除 key,這是為了保證與主節(jié)點(diǎn)數(shù)據(jù)的一致性,以便當(dāng)主從切換時(shí)后,仍然可以正常的處理過(guò)期 key。
不過(guò)當(dāng)系統(tǒng)中有大量頻繁過(guò)期的 key,且一個(gè)主節(jié)點(diǎn)有較多從節(jié)點(diǎn)的時(shí)候,這會(huì)帶來(lái)更多的內(nèi)存消耗。
關(guān)于主從集群中到期時(shí)間的處理,可以參照官方文檔 Redis replication sourl.cn/HRJygR 這部分內(nèi)容。
不過(guò)在文檔中似乎并沒(méi)有明確指出缺少這種“延遲刪除”的措施會(huì)導(dǎo)致怎樣的后果,只說(shuō)是為了不違反數(shù)據(jù)一致性 (don't violate the consistency of the data set)。

浙公網(wǎng)安備 33010602011771號(hào)