Redis可以為每個key設定過期時間,會將每個設定了過期時間的key放入一個獨立的字典中。【相關推薦:Redis視訊教學】
typedef struct redisDb { int id; //id是資料庫序號,為0-15(預設Redis有16個資料庫) long avg_ttl; //儲存的資料庫物件的平均ttl(time to live),用於統計 dict *dict; //儲存資料庫所有的key-value dict *expires; //儲存key的過期時間 dict *blocking_keys;//blpop 儲存阻塞key和使用者端物件 dict *ready_keys;//阻塞後push 響應阻塞使用者端 儲存阻塞後push的key和使用者端物件 dict *watched_keys;//儲存watch監控的的key和使用者端物件 } redisDb;
dict 用來維護一個 Redis 資料庫中包含的所有 Key-Value 鍵值對,expires則用於維護一個 Redis 資料庫中設定了失效時間的鍵(即key與失效時間的對映)。注意這裡的失效時間是用毫秒的時間戳表示的,比如2022-01-02 22:45:02過期則value為1641134702000
當我們使用expire命令設定一個key的失效時間時,Redis 首先到 dict 這個字典表中查詢要設定的key是否存在,如果存在就將這個key和失效時間新增到 expires 這個字典表。
當我們使用setex命令向系統插入資料時,Redis 首先將 Key 和 Value 新增到 dict 這個字典表中,然後將 Key 和失效時間新增到 expires 這個字典表中。注意setex只能用於字串。
簡單地總結來說就是,設定了失效時間的key和具體的失效時間全部都維護在 expires 這個字典表中。
expire的使用
expire命令的使用方法如下: expire key ttl(單位秒)
127.0.0.1:6379> expire name 2 #2秒失效 (integer) 1 127.0.0.1:6379> get name (nil) 127.0.0.1:6379> set name zhangfei OK 127.0.0.1:6379> ttl name #永久有效 (integer) -1 127.0.0.1:6379> expire name 30 #30秒失效 (integer) 1 127.0.0.1:6379> ttl name #還有24秒失效 (integer) 24 127.0.0.1:6379> ttl name #失效 (integer) -2
Redis有四個不同的命令可以用於設定鍵的生存時間(鍵可以生存多久)或過期時間(鍵什麼時候會被刪除):
expire 命令用於將鍵key的生存時間設定為ttl秒
pexpire 命令用於將鍵key的生存時間設定為ttl毫秒
expireat 命令用於將鍵key的過期時間設定為timestamp所指定的秒數時間戳
pexpireat 命令用於將鍵key的過期時間設定為timestamp所指定的毫秒數時間戳
注意expire、pexpire、expireat最終實現都是通過pexpireat實現的,也就是說無論使用者端執行哪個命令,都會Redis都會轉換成pexpireat命令執行。所以expires字典中存的時間是用毫秒時間戳表示的鍵的過期時間。
如果一個鍵過期了,那什麼時候被刪除呢?
有三種過期策略
定時刪除
1、對記憶體最友好:通過使用定時器,可以保證過期的鍵會盡可能快地被刪除,釋放所佔記憶體
1、對cpu最不友好:在過期鍵比較多的情況下,刪除過期鍵這一行為可能會佔用相當一部分cpu的時間,對伺服器的響應時間和吞吐量造成影響。
惰性刪除
1、對cpu最友好:只有在取出鍵的時候才會對過期鍵進行檢查,即不需要cpu定期掃描,也不需要建立大量的定時器。
1、對記憶體最不友好:如果一個鍵已經過期,但是後面不會被存取到的話,那麼就一直保留在資料庫中。如果這樣的鍵過多,無疑會佔用很大的記憶體。
定期刪除
定期刪除是上面的定時刪除和惰性刪除的一中折中方案。
1、定期刪除每隔一段時間執行一次過期鍵操作,並通過限制刪除操作執行的時長和頻率來減少刪除操作對cpu時間的影響。
2、通過刪除過期鍵,能有效的減少因為過期鍵而帶來的記憶體浪費
1、如果刪除操作執行得太頻繁,或者執行的時間太長,定期刪除策略就會退化成定時刪除,以至於佔用太多cpu的執行時間。
2、如果刪除操作執行的時間太少,或執行時間太短,定期刪除策略又會和惰性刪除一樣,出現記憶體浪費。
Redis使用是惰性刪除和定期刪除兩種策略:通過配好使用這兩種策略,伺服器可以很好地在合理使用cpu時間和避免浪費記憶體空間之間取得平衡。
惰性刪除策略的實現
過期鍵的惰性刪除刪除策略由db.c/expireIfNeeded函數實現,所有讀寫資料庫的Redis命令在執行之前都會呼叫expireIfNeed函數對輸入鍵進行檢查:
命令呼叫expireIfNeeded函數過程如下圖
另外因為每個被存取的鍵都可能被刪除,所以每個命令都必須能同時處理鍵存在以及不存在的情況。 下圖表示get命令的執行過程
定期刪除策略的實現
過期鍵的定期刪除策略由redis.c/activeExpireCycle函數實現,每當Redis的伺服器週期性操作redis.c/serverCron函數執行時,activeExpireCycle函數就會被呼叫,它在規定時間內,分多次遍歷伺服器中各個資料庫。
Redis 預設每秒進行 10 次過期掃描,過期掃描不會遍歷過期字典中所有的 key, 而是採用了一種簡單的貪心策略,步驟如下。
(1)從過期字典中隨機選出 20個 key。
(2)刪除這 20 個 key 中已經過期的 key。
(3)如果過期的 key的比例超過 1/4,那就重複步驟 (1)。 同時,為了保證過期掃描不會出現迴圈過度,導致結程卡死的現象,演演算法還增加了掃描時間的上限,預設不會超過 25ms。
假設一個大型的 Redis 範例中所有的 key 在同一時間過期了,會出現怎樣的結果呢?
消耗cpu
Redis 會持續掃描過期字典(迴圈多次),直到過期字典中過期的key變得稀疏,才會停止(迴圈次數明顯下降)。
導致請求卡頓或超時
當用戶端請求到來時,伺服器如果正好進入過期掃描狀態,使用者端的請求將會等待至少 25ms 後才會進行處理,如果使用者端將超時時間設定得比較短,比如 10ms,那麼就會出現大量的連線因為超時而關閉 ,業務端就會出現很多異常
所以一定要注意過期時間,如果有大批次的key過期,要給過期時間設定一個隨機範圍,而不能全部在同一時間過期。
更多程式設計相關知識,請存取:!!
以上就是一文聊聊Redis中的過期策略的詳細內容,更多請關注TW511.COM其它相關文章!