Redis系列之----Redis的過期設定及淘汰策略
Redis的過期時間機制和記憶體淘汰策略
Redis的資料是儲存在記憶體中的,而伺服器的記憶體大小是有限制的,除非宕機,否則這些資料會一直存在,對於一些不再使用的key,也應當進行刪除,否則會浪費記憶體空間。而且有些場景需要這種有失效性的資料,比如限時優惠活動、使用者session、驗證碼等。過了一定的時間就需要刪除這些資料。為了解決這個問題,Redis提供了可以為這個值設定一個過期的時間功能,當達到這個過期時間後,將這個資料進行刪掉來釋放記憶體空間。
一、過期時間機制
redis對儲存值的過期處理實際上是針對該值的鍵(key)處理的,即時間的設定也是設定key的有效時間。Expires字典儲存了所有鍵的過期時間,Expires也被稱為過期欄位。Redis提供了四種處理策略:
- EXPIRE 將key的生存時間設定為ttl秒
- PEXPIRE 將key的生成時間設定為ttl毫秒
- EXPIREAT 將key的過期時間設定為timestamp所代表的的秒數的時間戳
- PEXPIREAT 將key的過期時間設定為timestamp所代表的的毫秒數的時間戳
1、2兩種方式是設定一個過期的時間段,如處理驗證碼最常用的策略,設定三分鐘或五分鐘後失效,把分鐘數轉換成秒或毫秒儲存到redis中。
3、4兩種方式是指定一個過期的時間 ,比如優惠券的過期時間是某年某月某日,只是單位不一樣。
例如:
127.0.0.1:6379> set messageCode 8223 OK 127.0.0.1:6379> expire messageCode 100 (integer) 1
使用ttl命令檢視剩餘生命週期:
127.0.0.1:6379> ttl messageCode
(integer) 91
在小於2.1.3的版本里,只能對key設定一次expire。2.1.3和之後的版本里,可以多次對key使用expire命令,更新key的expire time。如果對key使用set或del命令,那麼也會移除expire time。
設定了過期時間的key會被在過期後會被刪除掉,那麼redis是以何種方式刪掉的呢?
刪除策略
Redis提供了三種不同的刪除策略:
- 定時刪除:在設定鍵的過期時間的時候建立一個定時器,當過期時間到的時候立馬執行刪除操作,此種方式對 正常業務讀寫影響較大。
- 定期刪除:redis資料庫預設每隔100ms就會進行隨機抽取一些設定過期時間的key進行檢測,過期則刪除。
- 惰性刪除:惰性刪除是為了堆定期刪除的時候沒有被抽取到的key進行刪除,當訪問這個key的時候,才會刪除這個key,所以叫惰性刪除。
目前,Redis採用的是惰性刪除+定期刪除的方案。
二、Redis的記憶體淘汰策略
前面說到,redis的會對過期的key進行清除,但是如果插入的速度大於清除的速度的話,伺服器的記憶體遲早會滿的,這個時候,就需要一種淘汰策略來對記憶體中的key再進行處理,Redis會根據使用者配置的淘汰策略清除無用的key釋放來記憶體空間,redis提供了以下八種中記憶體淘汰的策略:
- volatile-lru: 從設定過期時間的資料集中挑選出最近最少使用的資料淘汰。沒有設定過期時間的key不會被淘汰,這樣就可以在增加記憶體空間的同時保證需要持久化的資料不會丟失。
- allkeys-lru:從資料集中挑選最近最少使用的資料淘汰,該策略要淘汰的key面向的是全體key集合,而非過期的key集合。
- volatile-random:從已設定過期時間的資料集中隨機選擇資料淘汰
- allkeys-random:從全體的key集合中任意選擇資料淘汰
- volatile-ttl:除了淘汰機制採用LRU,策略基本上與volatile-lru相似,從設定過期時間的資料集中挑選將要過期的資料淘汰,ttl值越大越優先被淘汰。
- noeviction:禁止驅逐資料,也就是當記憶體不足以容納新入資料時,新寫入操作就會報錯,請求可以繼續進行,線上任務也不能持續進行,採用no-enviction策略可以保證資料不被丟失,這也是系統預設的一種淘汰策略。
- volatile-lfu:從所有配置了過期時間的鍵中驅逐使用頻率最少的鍵
- allkeys-lfu:從所有鍵中驅逐使用頻率最少的鍵。
這裡有個特點,所有的設定的過期了的key的淘汰策略名稱都是volatile開頭的,這是因為設定了過期時間的key都是屬於不穩定的key,這裡也是見名知意。
Redis中的LRU與常規的LRU實現並不相同,常規LRU會準確的淘汰掉隊頭的元素,但是Redis的LRU並不維護佇列,只是根據配置的策略要麼從所有的key中隨機選擇N個(N可以配置)要麼從所有的設定了過期時間的key中選出N個鍵,然後再從這N個鍵中選出最久沒有使用的一個key進行淘汰。LRU的最近最少使用實際上並不精確,為了解決這個缺陷,Redis4.0提供了LFU(Least Frequently Used)演算法,也就是最頻繁被訪問的資料將來最有可能被訪問到。思想類似redis會每個key維護一個計數器。每次key被訪問的時候,計數器增大。計數器越大,可以約等於訪問越頻繁。
如何設定過期淘汰策略
在redis的redis.conf檔案中,有這樣一段描述:
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
#
# volatile-lru -> Evict using approximated LRU among the keys with an expire set.
# allkeys-lru -> Evict any key using approximated LRU.
# volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
# allkeys-lfu -> Evict any key using approximated LFU.
# volatile-random -> Remove a random key among the ones with an expire set.
# allkeys-random -> Remove a random key, any key.
# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
# noeviction -> Don't evict anything, just return an error on write operations.
# LRU是最近最少使用
# LRU means Least Recently Used
# LFU是最少使用
# LFU means Least Frequently Used
#
# Both LRU, LFU and volatile-ttl are implemented using approximated
# randomized algorithms.
#
# Note: with any of the above policies, Redis will return an error on write
# operations, when there are no suitable keys for eviction.
#
# At the date of writing these commands are: set setnx setex append
# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
# getset mset msetnx exec sort
#
# The default is:
# 預設使用的是noeviction
# maxmemory-policy noeviction
這段配置檔案描述的很清楚,只需要看一下便能瞭解這些配置的含義。
一般來說,volatile-lru使用的場景偏多,在開發中,對於那些重要的,絕對不能丟棄的資料(如配置類資料等),應不設定有效期,這樣Redis就永遠不會淘汰這些資料;對於那些相對不是那麼重要的,並且能夠回源的資料,可以設定有效期,這樣在記憶體不夠時Redis就會淘汰這部分資料。
設定淘汰策略:
到redis的bin目錄下使用如下命令:
redis-cli -p 6379 config set maxmemory-policy volatile-lru
或者直接修改redis.conf配置:
maxmemory-policy volatile-lru
Redis中的淘汰機制是從效能和可用性方面考慮的,並不是完全可靠,我們在開發中應儘量對不需要永久儲存的資料主動設定或更新key的expire時間,主動刪除這部分資料,提升Redis整體效能和空間