天天看點

Redis緩存過期和淘汰政策

題記:

文章内容輸出來源:拉勾教育Java高薪訓練營。

本篇文章是 Redis 學習課程中的一部分筆記。

Redis緩存過期和淘汰政策

Redis性能高:

官方資料

讀:110000次/s

寫:81000次/s

長期使用,key會不斷增加,Redis作為緩存使用,實體記憶體也會滿。記憶體與硬碟交換(swap) 虛拟記憶體 ,頻繁IO 性能急劇下降

一、maxmemory

1、不設定的場景

Redis的key是固定的,不會增加

Redis作為DB使用,保證資料的完整性,不能淘汰 , 可以做叢集,橫向擴充

緩存淘汰政策:禁止驅逐 (預設)

2、設定的場景

Redis是作為緩存使用,不斷增加Key

maxmemory : 預設為0 不限制

問題:超過實體記憶體後性能急劇下架,甚至崩潰。記憶體與硬碟交換(swap) 虛拟記憶體 ,頻繁IO 性能急劇下降

設定多少?與業務有關

1個Redis執行個體,保證系統運作 1 G ,剩下的就都可以設定Redis

如:實體記憶體的3/4

slaver : 留出一定的記憶體

在redis.conf中配置

maxmemory 1024mb
           

指令: 獲得maxmemory數

CONFIG GET maxmemory
           

設定maxmemory後,當趨近maxmemory時,通過緩存淘汰政策,從記憶體中删除對象

二、expire

1、expire資料結構

在Redis中可以使用expire指令設定一個鍵的存活時間(ttl: time to live),過了這段時間,該鍵就會自動被删除。

2、expire的使用

expire指令的使用方法如下:

expire key ttl(機關秒)

Redis緩存過期和淘汰政策

3、expire原理

Redis緩存過期和淘汰政策

上面的代碼是Redis 中關于資料庫的結構體定義,這個結構體定義中除了 id 以外都是指向字典的指針,其中我們隻看 dict 和 expires。

dict 用來維護一個 Redis 資料庫中包含的所有 Key-Value 鍵值對,expires則用于維護一個 Redis 資料庫中設定了失效時間的鍵(即key與失效時間的映射)。

當我們使用 expire指令設定一個key的失效時間時,Redis 首先到 dict 這個字典表中查找要設定的key是否存在,如果存在就将這個key和失效時間添加到 expires 這個字典表。

當我們使用 setex指令向系統插入資料時,Redis 首先将 Key 和 Value 添加到 dict 這個字典表中,然後将 Key 和失效時間添加到 expires 這個字典表中。

簡單地總結來說就是,設定了失效時間的key和具體的失效時間全部都維護在 expires 這個字典表中。

三、删除政策

Redis的資料删除有定時删除、惰性删除和主動删除三種方式。

Redis目前采用惰性删除+主動删除的方式。

1、定時删除

在設定鍵的過期時間的同時,建立一個定時器,讓定時器在鍵的過期時間來臨時,立即執行對鍵的删除操作。

需要建立定時器,而且消耗CPU,一般不推薦使用。

2、惰性删除

在key被通路時如果發現它已經失效,那麼就删除它。

調用expireIfNeeded函數,該函數的意義是:讀取資料之前先檢查一下它有沒有失效,如果失效了就删除它。

int expireIfNeeded(redisDb *db, robj *key) { 
	//擷取主鍵的失效時間 
	long long when = getExpire(db,key); 
	//假如失效時間為負數,說明該主鍵未設定失效時間(失效時間預設為-1),直接傳回0 
	if (when < 0) return 0; 
	//假如Redis伺服器正在從RDB檔案中加載資料,暫時不進行失效主鍵的删除,直接傳回0 
	if (server.loading) return 0; 
	... 
	//如果以上條件都不滿足,就将主鍵的失效時間與目前時間進行對比,如果發現指定的主鍵 
	//還未失效就直接傳回0 
	if (mstime() <= when) return 0; 
	//如果發現主鍵确實已經失效了,那麼首先更新關于失效主鍵的統計個數,然後将該主鍵失 
	//效的資訊進行廣播,最後将該主鍵從資料庫中删除 
	server.stat_expiredkeys++; 
	propagateExpire(db,key);
	return dbDelete(db,key); 
}
           

3、主動删除

在redis.conf檔案中可以配置主動删除政策,預設是no-enviction(不删除)

maxmemory-policy allkeys-lru
           

四、LRU

LRU (Least recently used) 最近最少使用,算法根據資料的曆史通路記錄來進行淘汰資料,其核心思想是“如果資料最近被通路過,那麼将來被通路的幾率也更高”。

最常見的實作是使用一個連結清單儲存緩存資料,詳細算法實作如下:

  • 新資料插入到連結清單頭部;
  • 每當緩存命中(即緩存資料被通路),則将資料移到連結清單頭部;
  • 當連結清單滿的時候,将連結清單尾部的資料丢棄。
  • 在Java中可以使用LinkHashMap(哈希連結清單)去實作LRU

讓我們以使用者資訊的需求為例,來示範一下LRU算法的基本思路:

1、假設我們使用哈希連結清單來緩存使用者資訊,目前緩存了4個使用者,這4個使用者是按照時間順序依次從連結清單右端插入的。

Redis緩存過期和淘汰政策

2、此時,業務方通路使用者5,由于哈希連結清單中沒有使用者5的資料,我們從資料庫中讀取出來,插入到緩存當中。這時候,連結清單中最右端是最新通路到的使用者5,最左端是最近最少通路的使用者1。

Redis緩存過期和淘汰政策

3、接下來,業務方通路使用者2,哈希連結清單中存在使用者2的資料,我們怎麼做呢?我們把使用者2從它的前驅節點和後繼節點之間移除,重新插入到連結清單最右端。這時候,連結清單中最右端變成了最新通路到的使用者2,最左端仍然是最近最少通路的使用者1。

Redis緩存過期和淘汰政策

4、接下來,業務方請求修改使用者4的資訊。同樣道理,我們把使用者4從原來的位置移動到連結清單最右側,并把使用者資訊的值更新。這時候,連結清單中最右端是最新通路到的使用者4,最左端仍然是最近最少通路的使用者1。

Redis緩存過期和淘汰政策

5、業務通路使用者6,使用者6在緩存裡沒有,需要插入到哈希連結清單。假設這時候緩存容量已經達到上限,必須先删除最近最少通路的資料,那麼位于哈希連結清單最左端的使用者1就會被删除掉,然後再把使用者6插入到最右端。

Redis緩存過期和淘汰政策

Redis的LRU 資料淘汰機制:

在伺服器配置中儲存了 lru 計數器 server.lrulock,會定時(redis 定時程式 serverCorn())更新,server.lrulock 的值是根據 server.unixtime 計算出來的。

另外,從 struct redisObject 中可以發現,每一個 redis 對象都會設定相應的 lru。可以想象的是,每一次通路資料的時候,會更新 redisObject.lru。

LRU 資料淘汰機制是這樣的:在資料集中随機挑選幾個鍵值對,取出其中 lru 最大的鍵值對淘汰。不可能周遊key 用目前時間-最近通路 越大,說明通路間隔時間越長

volatile-lru

從已設定過期時間的資料集(server.db[i].expires)中挑選最近最少使用的資料淘汰

allkeys-lru

從資料集(server.db[i].dict)中挑選最近最少使用的資料淘汰

五、LFU

LFU (Least frequently used) 最不經常使用,如果一個資料在最近一段時間内使用次數很少,那麼在将來一段時間内被使用的可能性也很小。

六、random

随機

volatile-random

從已設定過期時間的資料集(server.db[i].expires)中任意選擇資料淘汰

allkeys-random

從資料集(server.db[i].dict)中任意選擇資料淘汰

七、ttl

volatile-ttl

從已設定過期時間的資料集(server.db[i].expires)中挑選将要過期的資料淘汰

redis 資料集資料結構中儲存了鍵值對過期時間的表,即 redisDb.expires。

TTL 資料淘汰機制:從過期時間的表中随機挑選幾個鍵值對,取出其中 ttl 最小的鍵值對淘汰。

八、noenviction

禁止驅逐資料,不删除 預設

緩存淘汰政策的選擇

  • allkeys-lru : 在不确定時一般采用政策。
  • volatile-lru : 比allkeys-lru性能差 存 : 過期時間
  • allkeys-random : 希望請求符合平均分布(每個元素以相同的機率被通路)
  • 自己控制:volatile-ttl 緩存穿透

案例分享:字典庫失效

key-Value 業務表存 code 顯示 文字

早期将字典庫,設定了maxmemory,并設定緩存淘汰政策為allkeys-lru,結果造成字典庫某些字段失效,緩存擊穿 , DB壓力劇增,差點當機。

分析:

maxmemory設定較小,采用allkeys-lru,會對沒有經常通路的字典庫随機淘汰,當再次通路時會緩存擊穿,請求會打到DB上。

解決方案:

1、不設定maxmemory

2、使用noenviction政策

Redis是作為DB使用的,要保證資料的完整性,是以不能删除資料。

可以将原始資料源(XML)在系統啟動時一次性加載到Redis中。

至此,結束

最後

在這個知識付費的時代,每一位熱愛技術分享、奮筆直書的人,都值得我們尊敬!是以,請不要吝啬您手中的滑鼠,按下左鍵,為小編點個贊吧。

更多内容,請關注微信公衆号:架構視角

特别鳴謝

感謝老貓老師風趣幽默的講解,讓我對所學知識點記憶深刻!

感謝木槿導師的認真和負責,每一次作業點評都是我前進的動力!

感謝班主任畢老師的負責和耐心,每次不厭其煩的上課通知都是我不忘初心,保持良好學習狀态的精神支柱!

感謝拉勾教育平台,給我這次花少量的錢就能報名第一期拉鈎訓練營,就能學習到很多深層次的技術精華的機會。而且,在學習過程中還認識了很多技術大佬,可以請教他們一些問題,比如張大佬、盧大佬、雨生大佬等等。

繼續閱讀