Redis對於過時鍵有三種清除策略:redis
惰性刪除算法
只有key被操做時(如GET),REDIS纔會被動檢查該key是否過時,若是過時則刪除之而且返回NIL。
一、這種刪除策略對CPU是友好的
,刪除操做只有在不得不的狀況下才會進行,不會對其餘的expire key上浪費無謂的CPU時間。
二、可是這種策略對內存不友好
,一個key已通過期,可是在它被操做以前不會被刪除,仍然佔據內存空間。若是有大量的過時鍵存在可是又不多被訪問到,那會形成大量的內存空間浪費。expireIfNeeded(redisDb *db, robj *key)函數位於src/db.c。
三、但僅是這樣是不夠的,由於可能存在一些key永遠不會被再次訪問到
,這些設置了過時時間的key也是須要在過時後被刪除的,咱們甚至能夠將這種狀況看做是一種內存泄露—-無用的垃圾數據佔用了大量的內存,而服務器卻不會本身去釋放它們,這對於運行狀態很是依賴於內存的Redis服務器來講,確定不是一個好消息。數據庫
按期刪除緩存
先說一下時間事件,對於持續運行的服務器來講,服務器須要按期對自身的資源和狀態進行必要的檢查和整理,從而讓服務器維持在一個健康穩定的狀態,這類操做被統稱爲常規操做(cron job)服務器
在 Redis中,常規操做由 redis.c/serverCron實現,它主要執行如下操做框架
Redis 將 serverCron 做爲時間事件來運行, 從而確保它每隔一段時間就會自動運行一次,又由於 serverCron 須要在 Redis 服務器運行期間一直按期運行,因此它是一個循環時間事件:serverCron 會一直按期執行,直到服務器關閉爲止。dom
在 Redis 2.6版本中,程序規定 serverCron每秒運行10次,平均每 100 毫秒運行一次。從Redis 2.8開始,用戶能夠經過修改hz選項來調整 serverCron的每秒執行次數,具體信息請參考redis.conf文件中關於 hz 選項的說明也叫定時刪除,這裏的「按期」指的是Redis按期觸發的清理策略,由位於src/redis.c的activeExpireCycle(void)函數來完成。函數
serverCron是由redis的事件框架驅動的定位任務,這個定時任務中會調用activeExpireCycle函數,針對每一個db在限制的時間REDIS_EXPIRELOOKUPS_TIME_LIMIT內遲可能多的刪除過時key,之因此要限制時間是爲了防止過長時間 的阻塞影響redis的正常運行。這種主動刪除策略彌補了被動刪除策略在內存上的不友好。測試
所以,Redis會週期性的隨機測試一批設置了過時時間的key並進行處理。測試到的已過時的key將被刪除。典型的方式爲,Redis每秒作10次以下的步驟:spa
這是一個基於機率的簡單算法,基本的假設是抽出的樣本可以表明整個key空間,redis持續清理過時的數據直至將要過時的key的百分比降到了25%如下。這也意味着在任何給定的時刻已通過期但仍佔據着內存空間的key的量最多爲每秒的寫操做量除以4.
Redis-3.0.0中的默認值是10,表明每秒鐘調用10次後臺任務。
除了主動淘汰的頻率外,Redis對每次淘汰任務執行的最大時長也有一個限定,這樣保證了每次主動淘汰不會過多阻塞應用請求,如下是這個限定計算公式:
#define ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 25 /* CPU max % for keys collection */ ... timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/server.hz/100;
hz調大將會提升Redis主動淘汰的頻率,若是你的Redis存儲中包含不少冷數據佔用內存過大的話,能夠考慮將這個值調大,但Redis做者建議這個值不要超過100。咱們實際線上將這個值調大到100,觀察到CPU會增長2%左右,但對冷數據的內存釋放速度確實有明顯的提升(經過觀察keyspace個數和used_memory大小)。
能夠看出timelimit和server.hz是一個倒數的關係,也就是說hz配置越大,timelimit就越小。換句話說是每秒鐘指望的主動淘汰頻率越高,則每次淘汰最長佔用時間就越短。這裏每秒鐘的最長淘汰佔用時間是固定的250ms(1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/100),而淘汰頻率和每次淘汰的最長時間是經過hz參數控制的。
從以上的分析看,當redis中的過時key比率沒有超過25%以前,提升hz能夠明顯提升掃描key的最小個數。假設hz爲10,則一秒內最少掃描200個key(一秒調用10次*每次最少隨機取出20個key),若是hz改成100,則一秒內最少掃描2000個key;另外一方面,若是過時key比率超過25%,則掃描key的個數無上限,可是cpu時間每秒鐘最多佔用250ms。
當REDIS運行在主從模式時,只有主結點纔會執行上述這兩種過時刪除策略,而後把刪除操做」del key」同步到從結點。
maxmemory
當前已用內存超過maxmemory限定時,觸發主動清理策略
若是某些key沒有觸發惰性刪除,也就是說通過惰性刪除 + 按期刪除兩輪清理,依舊存在,該如何解決呢?這種狀況,在內存不足的時候,Redis的主動清理策略就派上用場了,即MEMORY MANAGEMENT
* volatile-lru:在那些設置了expire過時時間的緩存中,清除最少用的舊緩存,而後保存新的緩存 * allkeys-lru:清除最少用的舊緩存,而後保存新的緩存 * volatile-lfu:在那些設置了expire過時時間的緩存中,清除最長時間未用的舊緩存,而後保存新的緩存 * allkeys-lfu:清除最長時間未用的舊緩存,而後保存新的緩存 * volatile-random:在那些設置了expire過時時間的緩存中,隨機刪除緩存 * allkeys-random:在全部的緩存中隨機刪除(不推薦) * volatile-ttl:在那些設置了expire過時時間的緩存中,刪除即將過時的 * noeviction:舊緩存永不過時,新緩存設置不了,返回錯誤
當mem_used內存已經超過maxmemory的設定,對於全部的讀寫請求,都會觸發redis.c/freeMemoryIfNeeded(void)函數以清理超出的內存。注意這個清理過程是阻塞的,直到清理出足夠的內存空間。因此若是在達到maxmemory而且調用方還在不斷寫入的狀況下,可能會反覆觸發主動清理策略,致使請求會有必定的延遲。
清理時會根據用戶配置的maxmemory-policy來作適當的清理(通常是LRU或TTL),這裏的LRU或TTL策略並非針對redis的全部key,而是以配置文件中的maxmemory-samples個key做爲樣本池進行抽樣清理。
maxmemory-samples在redis-3.0.0中的默認配置爲5,若是增長,會提升LRU或TTL的精準度,redis做者測試的結果是當這個配置爲10時已經很是接近全量LRU的精準度了,而且增長maxmemory-samples會致使在主動清理時消耗更多的CPU時間,建議:
這裏提一句,實際上redis根本就不會準確的將整個數據庫中最久未被使用的鍵刪除,而是每次從數據庫中隨機取5個鍵並刪除這5個鍵裏最久未被使用的鍵。上面提到的全部的隨機的操做實際上都是這樣的,這個5能夠用過redis的配置文件中的maxmemeory-samples參數配置。
Replication link和AOF文件中的過時處理
爲了得到正確的行爲而不至於致使一致性問題,當一個key過時時DEL操做將被記錄在AOF文件並傳遞到全部相關的slave。也即過時刪除操做統一在master實例中進行並向下傳遞,而不是各salve各自掌控。這樣一來便不會出現數據不一致的情形。當slave鏈接到master後並不能當即清理已過時的key(須要等待由master傳遞過來的DEL操做),slave仍需對數據集中的過時狀態進行管理維護以便於在slave被提高爲master會能像master同樣獨立的進行過時處理。