1 設(shè)置帶過期時間的 key
# 時間復雜度:O(1),最常用方式
expire key seconds
# 字符串獨有方式
setex(String key, int seconds, String value)
????????除了string獨有設(shè)置過期時間的方法,其他類型都需依靠expire方法設(shè)置時間,若:
- 未設(shè)置時間,則緩存永不過期
- 設(shè)置過期時間,但之后又想讓緩存永不過期,使用persist
????????設(shè)置key的過期時間。超時后,將會自動刪除該key。在Redis的術(shù)語中一個key的相關(guān)超時是volatile的。
????????生存時間可以通過使用?DEL?命令來刪除整個?key?來移除,或者被?SET?和?GETSET?命令覆寫(overwrite)。這意味著,如果一個命令只是修改(alter)一個帶生存時間的?key?的值而不是用一個新的?key?值來代替(replace)它的話,那么生存時間不會被改變。 如使用?INCR?遞增key的值,執(zhí)行?LPUSH?將新值推到 list 中或用?HSET?改變hash的field,這些操作都使超時保持不變。
- 使用?PERSIST?命令可以清除超時,使其變成一個永久key;
- 若?key?被?RENAME?命令修改,相關(guān)的超時時間會轉(zhuǎn)移到新key;
- 若?key?被?RENAME?命令修改,比如原來就存在?Key_A,然后調(diào)用?RENAME Key_B Key_A?命令,這時不管原來?Key_A?是永久的還是設(shè)為超時的,都會由Key_B的有效期狀態(tài)覆蓋;
注意,使用非正超時調(diào)用 EXPIRE/PEXPIRE 或具有過去時間的 EXPIREAT/PEXPIREAT 將導致key被刪除而不是過期(因此,發(fā)出的key事件將是 del,而不是過期)。
1.1 刷新過期時間
????????對已經(jīng)有過期時間的key執(zhí)行EXPIRE操作,將會更新它的過期時間。有很多應(yīng)用有這種業(yè)務(wù)場景,例如記錄會話的session。
1.2 Redis 之前的 2.1.3 的差異
????????在 Redis 版本之前 2.1.3 中,使用更改其值的命令更改具有過期集的密鑰具有完全刪除key的效果。由于現(xiàn)在修復的復制層中存在限制,因此需要此語義。
????????EXPIRE 將返回 0,并且不會更改具有超時集的鍵的超時。
1.3 返回值
- 1,如果成功設(shè)置過期時間。
- 0,如果key不存在或者不能設(shè)置過期時間。
1.4 示例
????????假設(shè)有一 Web 服務(wù),對用戶最近訪問的最新 N 頁感興趣,這樣每個相鄰頁面視圖在上一個頁面之后不超過 60 秒。從概念上講,可以將這組頁面視圖視為用戶的導航會話,該會話可能包含有關(guān)ta當前正在尋找的產(chǎn)品的有趣信息,以便你可以推薦相關(guān)產(chǎn)品。
????????可使用以下策略輕松在 Redis 中對此模式建模:每次用戶執(zhí)行頁面視圖時,您都會調(diào)用以下命令:
MULTI
RPUSH pagewviews.user:<userid> http://.....
EXPIRE pagewviews.user:<userid> 60
EXEC
????????如果用戶空閑超過 60 秒,則將刪除該key,并且僅記錄差異小于 60 秒的后續(xù)頁面視圖。此模式很容易修改,使用 INCR 而不是使用 RPUSH 的列表。
1.5 帶過期時間的 key
????????通常,創(chuàng)建 Redis 鍵時沒有關(guān)聯(lián)的存活時間。key將永存,除非用戶以顯式方式(例如 DEL 命令)將其刪除。
????????EXPIRE 族的命令能夠?qū)⑦^期項與給定key關(guān)聯(lián),但代價是該key使用的額外內(nèi)存。當key具有過期集時,Redis 將確保在經(jīng)過指定時間時刪除該key。
????????可使用 EXPIRE 和 PERSIST 命令(或其他嚴格命令)更新或完全刪除生存的關(guān)鍵時間。
1.6 過期精度
????????在 Redis 2.4 中,過期可能不準確,并且可能介于 0 到 1 秒之間。
????????Redis 2.6,過期誤差從 0 到 1 毫秒。
1.7 過期和持久化
????????過期信息的鍵存儲為絕對 Unix 時間戳(Redis 版本 2.6 或更高版本為毫秒)。這意味著即使 Redis 實例不處于活動狀態(tài),時間也在流動。
????????要使過期工作良好,必須穩(wěn)定計算機時間。若將 RDB 文件從兩臺計算機上移動,其時鐘中具有大 desync,則可能會發(fā)生有趣的事情(如加載時加載到過期的所有key)。
????????即使運行時的實例,也始終會檢查計算機時鐘,例如,如果將一個key設(shè)置為 1000 秒,然后在將來設(shè)置計算機時間 2000 秒,則該key將立即過期,而不是持續(xù) 1000 秒。
2 過期策略
????????Redis 所有的數(shù)據(jù)結(jié)構(gòu)都可以設(shè)置過期時間,時間一到,就會自動刪除。你可以想象 Redis 內(nèi)部有一個死神,時刻盯著所有設(shè)置了過期時間的 key,壽命一到就會立即收割。
??你還可以進一步站在死神的角度思考,會不會因為同一時間太多的 key 過期,以至于忙不過來。同時因為 Redis 是單線程的,收割的時間也會占用線程的處理時間,如果收割的太過于繁忙,會不會導致線上讀寫指令出現(xiàn)卡頓。在過期這件事上,Redis 非常小心。
2.1 過期 key 集合
??redis 會將每個設(shè)置了過期時間的 key 放入到一個獨立的字典中,以后會定時遍歷這個字典來刪除到期的 key。除了定時遍歷之外,它還會使用惰性策略來刪除過期的 key,所謂惰性策略就是在客戶端訪問這個 key 的時候,redis 對 key 的過期時間進行檢查,如果過期了就立即刪除。定時刪除是集中處理,惰性刪除是零散處理。
2.2 定時掃描策略
??Redis 默認會每秒進行十次過期掃描,過期掃描不會遍歷過期字典中所有的 key,而是采用了一種簡單的貪心策略。
- 從過期字典中隨機 20 個 key;
- 刪除這 20 個 key 中已經(jīng)過期的 key;
- 如果過期的 key 比率超過 1/4,那就重復步驟 1;
??同時,為了保證過期掃描不會出現(xiàn)循環(huán)過度,導致線程卡死現(xiàn)象,算法還增加了掃描時間的上限,默認不會超過 25ms。
??設(shè)想一個大型的 Redis 實例中所有的 key 在同一時間過期了,會出現(xiàn)怎樣的結(jié)果?
??毫無疑問,Redis 會持續(xù)掃描過期字典 (循環(huán)多次),直到過期字典中過期的 key 變得稀疏,才會停止 (循環(huán)次數(shù)明顯下降)。這就會導致這期間線上讀寫 QPS 下降明顯。還有另外一種原因是內(nèi)存管理器需要頻繁回收內(nèi)存頁,這也會產(chǎn)生一定的 CPU 消耗。
??這里解析一下,假如單臺 Redis 讀寫請求 QPS 是 10w,也就是每個請求需要 0.00001s 來完成,每秒執(zhí)行十次過期掃描,每次過期掃描都達到上限 25ms,那么每秒過期掃描總花費 0.25s,相當于 QPS 降低了 2.5W。
??所以業(yè)務(wù)開發(fā)人員一定要注意過期時間,如果有大批量的 key 過期,要給過期時間設(shè)置一個隨機范圍,而不能全部在同一時間過期。
#在目標過期時間上增加一天的隨機時間
redis.expire_at(key, random.randint(86400) + expire_ts)
2.3 從庫的過期策略
??從庫不會進行過期掃描,從庫對過期的處理是被動的。主庫在 key 到期時,會在 AOF 文件里增加一條 del 指令,同步到所有的從庫,從庫通過執(zhí)行這條 del 指令來刪除過期的 key。
??因為指令同步是異步進行的,所以主庫過期的 key 的 del 指令沒有及時同步到從庫的話,會出現(xiàn)主從數(shù)據(jù)的不一致,主庫沒有的數(shù)據(jù)在從庫里還存在。
2.4 懶惰刪除策略
????????Redis 為什么要懶惰刪除(lazy free)?
????????刪除指令 del 會直接釋放對象的內(nèi)存,大部分情況下,這個指令非???,沒有明顯延遲。不過如果刪除的 key 是一個非常大的對象,比如一個包含了千萬元素的 hash,又或者在使用 FLUSHDB 和 FLUSHALL 刪除包含大量鍵的數(shù)據(jù)庫時,那么刪除操作就會導致單線程卡頓。
????????redis 4.0 引入了 lazyfree 的機制,它可以將刪除鍵或數(shù)據(jù)庫的操作放在后臺線程里執(zhí)行, 從而盡可能地避免服務(wù)器阻塞。
2.4.1 unlink指令
????????unlink 指令,它能對刪除操作進行懶處理,丟給后臺線程來異步回收內(nèi)存。
unlink key
OK
2.4.2 flush指令
????????flushdb 和 flushall 指令,用來清空數(shù)據(jù)庫,這也是極其緩慢的操作。Redis 4.0 同樣給這兩個指令也帶來了異步化,在指令后面增加 async 參數(shù)就可以將整棵大樹連根拔起,扔給后臺線程慢慢焚燒。
flushall async
OK
2.4.3 異步隊列
????????主線程將對象的引用從「大樹」中摘除后,會將這個 key 的內(nèi)存回收操作包裝成一個任務(wù),塞進異步任務(wù)隊列,后臺線程會從這個異步隊列中取任務(wù)。任務(wù)隊列被主線程和異步線程同時操作,所以必須是一個線程安全的隊列。 不是所有的 unlink 操作都會延后處理,如果對應(yīng) key 所占用的內(nèi)存很小,延后處理就沒有必要了,這時候 Redis 會將對應(yīng)的 key 內(nèi)存立即回收,跟 del 指令一樣。
2.5 惰性刪除流程
????????在進行g(shù)et或setnx等操作時,先檢查key是否過期:
- 若過期,刪除key,然后執(zhí)行相應(yīng)操作;
- 若沒過期,直接執(zhí)行相應(yīng)操作;
2.5.1 RDB處理過期key
????????過期key對RDB無影響:
- 從內(nèi)存數(shù)據(jù)庫持久化數(shù)據(jù)到RDB文件,持久化key之前,會檢查是否過期,過期的key不進入RDB文件;
- 從RDB文件恢復數(shù)據(jù)到內(nèi)存數(shù)據(jù)庫,數(shù)據(jù)載入數(shù)據(jù)庫之前,會對Key進行過期檢查,若過期,不導入數(shù)據(jù)庫(主庫情況);
2.5.2 AOF處理過期Key
????????過期key對AOF沒有任何影響。
2.5.2.1 從內(nèi)存數(shù)據(jù)庫持久化到AOF文件
- 當key過期后,還沒有被刪除,此時進行執(zhí)行持久化操作(該key不會進入aof文件,因為沒有發(fā)生修改命令)
- 當key過期后,在發(fā)生刪除操作時,程序會向aof文件追加一條del命令(在將來的以aof文件恢復數(shù)據(jù)的時候該過期的鍵就會被刪掉)
2.5.2.2 AOF重寫
????????重寫時,會先判斷key是否過期,已過期的key不會重寫到aof文件
2.5.2.3 在復制鏈路和 AOF 文件中處理過期的方式
????????為了在不犧牲一致性的情況下獲得正確行為,當key過期時,DEL 操作將同時在 AOF 文件中合成并獲取所有附加的從節(jié)點。這樣,過期的這個處理過程集中到主節(jié)點中,還沒有一致性錯誤的可能性。
????????但是,雖然連接到主節(jié)點的從節(jié)點不會獨立過期key(但會等待來自master的 DEL),但它們?nèi)詫⑹褂脭?shù)據(jù)集中現(xiàn)有過期的完整狀態(tài),因此,當選擇slave作為master時,它將能夠獨立過期key,完全充當master。
????????默認每臺Redis服務(wù)器有16個數(shù)據(jù)庫,默認使用0號數(shù)據(jù)庫,所有的操作都是對0號數(shù)據(jù)庫的操作
# 設(shè)置數(shù)據(jù)庫數(shù)量。默認為16個庫,默認使用DB 0,可使用"select 1"來選擇一號數(shù)據(jù)庫
# 注意:由于默認使用0號數(shù)據(jù)庫,那么我們所做的所有的緩存操作都存在0號數(shù)據(jù)庫上,
# 當你在1號數(shù)據(jù)庫上去查找的時候,就查不到之前set過的緩存
# 若想將0號數(shù)據(jù)庫上的緩存移動到1號數(shù)據(jù)庫,可以使用"move key 1"
databases 16
- memcached只是用了惰性刪除,而redis同時使用了惰性刪除與定期刪除,這也是二者的一個不同點(可以看做是redis優(yōu)于memcached的一點)
- 對于惰性刪除而言,并不是只有獲取key的時候才會檢查key是否過期,在某些設(shè)置key的方法上也會檢查(eg.setnx key2 value2:該方法類似于memcached的add方法,如果設(shè)置的key2已經(jīng)存在,那么該方法返回false,什么都不做;如果設(shè)置的key2不存在,那么該方法設(shè)置緩存key2-value2。假設(shè)調(diào)用此方法的時候,發(fā)現(xiàn)redis中已經(jīng)存在了key2,但是該key2已經(jīng)過期了,如果此時不執(zhí)行刪除操作的話,setnx方法將會直接返回false,也就是說此時并沒有重新設(shè)置key2-value2成功,所以對于一定要在setnx執(zhí)行之前,對key2進行過期檢查)
????????可是,很多過期key,你沒及時去查,定期刪除也漏掉了,大量過期key堆積內(nèi)存,Redis內(nèi)存殆耗盡!因此內(nèi)存滿時,還需有內(nèi)存淘汰機制!這就是 Redis 自己?主動刪除?數(shù)據(jù)了!
3 Redis 內(nèi)存淘汰機制
????????Redis 數(shù)據(jù)庫可以通過配置文件來配置最大緩存,當寫入的數(shù)據(jù)發(fā)現(xiàn)沒有足夠的內(nèi)存可用的時候,Redis 會觸發(fā)內(nèi)存淘汰機制。Redis 為了滿足多樣化場景,提供了八種策略,可以在 redis.config 文件中配置:
- volatile-lru:從已設(shè)置過期時間的數(shù)據(jù)集中挑選最近最少使用的數(shù)據(jù)淘汰
- volatile-ttl:從已設(shè)置過期時間的數(shù)據(jù)集中挑選將要過期的數(shù)據(jù)淘汰
- volatile-random:從已設(shè)置過期時間的數(shù)據(jù)集中任意選擇數(shù)據(jù)淘汰
- volatile-lfu:從已設(shè)置過期時間的數(shù)據(jù)集中挑選使用頻率最低的數(shù)據(jù)淘汰
- allkeys-lru:從所有數(shù)據(jù)集中挑選最近最少使用的數(shù)據(jù)淘汰
- allkeys-lfu:從所有數(shù)據(jù)集中挑選使用頻率最低的數(shù)據(jù)淘汰
- allkeys-random:從所有數(shù)據(jù)集中任意選擇數(shù)據(jù)淘汰
- noenviction:不回收任何數(shù)據(jù),返回一個寫操作的錯誤信息。這也是默認策略
參考鏈接
Redis 詳解_羅志宏的博客-CSDN博客
Redis 的過期策略_redis key過期策略_胖虎·的博客-CSDN博客
Redis鍵過期策略詳解-云社區(qū)-華為云
詳細聊聊Redis的過期策略_Redis_腳本之家
Redis的過期策略以及內(nèi)存淘汰機制_redis過期策略以及內(nèi)存淘汰機制_Felix-Yuan的博客-CSDN博客文章來源:http://www.zghlxwxcb.cn/news/detail-703339.html
Redis 詳解_王叮咚的博客-CSDN博客文章來源地址http://www.zghlxwxcb.cn/news/detail-703339.html
到了這里,關(guān)于【云原生進階之PaaS中間件】第一章Redis-1.4過期策略的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!