題記:
文章内容輸出來源:拉勾教育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(機關秒)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPB1ENVR0T4FEVNBDOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLzITN2MTNykDMyIzNwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
3、expire原理
上面的代碼是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個使用者是按照時間順序依次從連結清單右端插入的。
2、此時,業務方通路使用者5,由于哈希連結清單中沒有使用者5的資料,我們從資料庫中讀取出來,插入到緩存當中。這時候,連結清單中最右端是最新通路到的使用者5,最左端是最近最少通路的使用者1。
3、接下來,業務方通路使用者2,哈希連結清單中存在使用者2的資料,我們怎麼做呢?我們把使用者2從它的前驅節點和後繼節點之間移除,重新插入到連結清單最右端。這時候,連結清單中最右端變成了最新通路到的使用者2,最左端仍然是最近最少通路的使用者1。
4、接下來,業務方請求修改使用者4的資訊。同樣道理,我們把使用者4從原來的位置移動到連結清單最右側,并把使用者資訊的值更新。這時候,連結清單中最右端是最新通路到的使用者4,最左端仍然是最近最少通路的使用者1。
5、業務通路使用者6,使用者6在緩存裡沒有,需要插入到哈希連結清單。假設這時候緩存容量已經達到上限,必須先删除最近最少通路的資料,那麼位于哈希連結清單最左端的使用者1就會被删除掉,然後再把使用者6插入到最右端。
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中。
至此,結束
最後
在這個知識付費的時代,每一位熱愛技術分享、奮筆直書的人,都值得我們尊敬!是以,請不要吝啬您手中的滑鼠,按下左鍵,為小編點個贊吧。
更多内容,請關注微信公衆号:架構視角
特别鳴謝
感謝老貓老師風趣幽默的講解,讓我對所學知識點記憶深刻!
感謝木槿導師的認真和負責,每一次作業點評都是我前進的動力!
感謝班主任畢老師的負責和耐心,每次不厭其煩的上課通知都是我不忘初心,保持良好學習狀态的精神支柱!
感謝拉勾教育平台,給我這次花少量的錢就能報名第一期拉鈎訓練營,就能學習到很多深層次的技術精華的機會。而且,在學習過程中還認識了很多技術大佬,可以請教他們一些問題,比如張大佬、盧大佬、雨生大佬等等。