Redis過(guò)期刪除策略與內(nèi)存淘汰策略
過(guò)期刪除策略
過(guò)期刪除策略: redis可以對(duì)key設(shè)置過(guò)期時(shí)間,因此要有相應(yīng)的機(jī)制將已過(guò)期的鍵值對(duì)刪除。
設(shè)置Redis中key的過(guò)期時(shí)間 (單位:秒)
- 1)
expire key time
這是最常用的方式 - 2)
setex key, seconds, value
字符串獨(dú)有的方式
如果未設(shè)置時(shí)間,那就是永不過(guò)期。 如果設(shè)置了過(guò)期時(shí)間,使用 persist key
讓key永不過(guò)期。
每當(dāng)我們對(duì)一個(gè) key 設(shè)置了過(guò)期時(shí)間,Redis 會(huì)把該 key 帶上過(guò)期時(shí)間存儲(chǔ)到一個(gè)過(guò)期字典(expires dict)中,也就是說(shuō)過(guò)期字典保存了數(shù)據(jù)庫(kù)中所有 key 的過(guò)期時(shí)間。
過(guò)期字典存儲(chǔ)在 redisDb 結(jié)構(gòu)中,如下:
typedef struct redisDb { dict *dict; /* 存放著所有的鍵值對(duì) */ dict *expires; /* 過(guò)期字典: 鍵和鍵的過(guò)期時(shí)間 */ .... } redisDb; /* 過(guò)期字典數(shù)據(jù)結(jié)構(gòu)結(jié)構(gòu)如下: 過(guò)期字典的 key 是一個(gè)指針,指向某個(gè)鍵對(duì)象; 過(guò)期字典的 value 是一個(gè) long long 類型的整數(shù),這個(gè)整數(shù)保存了 key 的過(guò)期時(shí)間; */
字典實(shí)際上是哈希表,哈希表的最大好處就是讓我們可以用 O(1) 的時(shí)間復(fù)雜度來(lái)快速查找。
當(dāng)我們查詢一個(gè) key 時(shí),Redis首先檢查該 key是否存在于過(guò)期字典中:
- 如果不在,則正常讀取鍵值(沒(méi)有設(shè)置過(guò)期時(shí)間)
- 如果存在,則會(huì)獲取該 key 的過(guò)期時(shí)間,然后與當(dāng)前系統(tǒng)時(shí)間進(jìn)行比對(duì),判定該 key是否過(guò)期
常見(jiàn)的三種過(guò)期刪除策略
- 定時(shí)刪除:在設(shè)置key的過(guò)期時(shí)間時(shí),同時(shí)創(chuàng)建一個(gè)定時(shí)事件,當(dāng)時(shí)間到達(dá)時(shí),由事件處理器自動(dòng)執(zhí)行key的刪除操作。
- 惰性刪除:不主動(dòng)刪除過(guò)期鍵,每次從數(shù)據(jù)庫(kù)訪問(wèn)key時(shí),都檢測(cè)key是否過(guò)期,如果過(guò)期則刪除該key。
- 定期刪除:每隔一段時(shí)間「隨機(jī)」從數(shù)據(jù)庫(kù)中取出一定數(shù)量的key進(jìn)行檢查,并刪除其中的過(guò)期key。
Redis使用用的過(guò)期刪除策略
Redis 采用了 惰性刪除 + 定期刪除 的方式處理過(guò)期數(shù)據(jù),以求在合理使用 CPU 時(shí)間和避免內(nèi)存浪費(fèi)之間取得平衡 。
- 惰性刪除的使用:當(dāng)我們?cè)L問(wèn)一個(gè)key時(shí),會(huì)先檢查這個(gè)key是否過(guò)期,如果過(guò)期則刪除這個(gè)key并返回給客戶端null,否則返回對(duì)應(yīng)value
- 定期刪除的使用:定期檢查一次數(shù)據(jù)庫(kù),此配置可通過(guò) Redis 的配置文件 redis.conf 進(jìn)行配置,配置鍵為 hz 它的默認(rèn)值是 hz 10( 從數(shù)據(jù)庫(kù)中隨機(jī)抽取20個(gè)key 進(jìn)行過(guò)期檢查 )
Redis的定期刪除的流程
- 從過(guò)期字典中隨機(jī)抽取 20 個(gè) key(20是寫死在代碼中的,不可修改)
- 檢查這 20個(gè)key是否過(guò)期,并刪除過(guò)期的 key
- 如果本輪檢查的已過(guò)期 key 的數(shù)量,超過(guò) 5 個(gè)(過(guò)期 key 的數(shù)量 / 20 > 25%),則再次抽取20個(gè)檢查檢查,如果某一次該比例小于 25%,則結(jié)束檢查,然后等待下一輪再檢查
Redis 為了保證定期刪除不會(huì)出現(xiàn)循環(huán)過(guò)度,導(dǎo)致線程卡死現(xiàn)象,為此增加了定期刪除循環(huán)流程的時(shí)間上限,默認(rèn)不會(huì)超過(guò) 25ms(超過(guò)就停止檢查)。
內(nèi)存淘汰策略
內(nèi)存淘汰策略:redis 的運(yùn)行內(nèi)存已經(jīng)超過(guò)redis設(shè)置的最大內(nèi)存后,會(huì)使用內(nèi)存淘汰策略刪除符合條件的 key,以此來(lái)保障 Redis 高效的運(yùn)行。
設(shè)置Redis最大運(yùn)行內(nèi)存
在配置文件 redis.conf 中,可以通過(guò)參數(shù) maxmemory
來(lái)設(shè)定最大運(yùn)行內(nèi)存,只有在 Redis 的運(yùn)行內(nèi)存達(dá)到了我們?cè)O(shè)置的最大運(yùn)行內(nèi)存,才會(huì)觸發(fā)內(nèi)存淘汰策略。
不同位數(shù)的操作系統(tǒng),maxmemory 的默認(rèn)值是不同的:
- 在 64 位操作系統(tǒng)中,maxmemory 的默認(rèn)值是 0,表示沒(méi)有內(nèi)存大小限制,那么不管用戶存放多少數(shù)據(jù)到 Redis 中,Redis 也不會(huì)對(duì)可用內(nèi)存進(jìn)行檢查,直到 Redis 實(shí)例因內(nèi)存不足而崩潰也無(wú)作為。
- 在 32 位操作系統(tǒng)中,maxmemory 的默認(rèn)值是 3G,因?yàn)?32 位的機(jī)器最大只支持 4GB 的內(nèi)存,而系統(tǒng)本身就需要一定的內(nèi)存資源來(lái)支持運(yùn)行,所以 32 位操作系統(tǒng)限制最大 3 GB 的可用內(nèi)存是非常合理的,這樣可以避免因?yàn)閮?nèi)存不足而導(dǎo)致 Redis 實(shí)例崩潰
Redis 內(nèi)存淘汰策略有哪些?
Redis 內(nèi)存淘汰策略共有八種,這八種策略大體分為「不進(jìn)行數(shù)據(jù)淘汰」和「進(jìn)行數(shù)據(jù)淘汰」兩類策略
不進(jìn)行數(shù)據(jù)淘汰的策略:
- noeviction(Redis3.0之后,默認(rèn)的內(nèi)存淘汰策略):它表示當(dāng)運(yùn)行內(nèi)存超過(guò)最大設(shè)置內(nèi)存時(shí),不淘汰任何數(shù)據(jù),而是不再提供服務(wù),直接返回錯(cuò)誤
- 進(jìn)行數(shù)據(jù)淘汰的策略( 又可以細(xì)分為在設(shè)置了過(guò)期時(shí)間的數(shù)據(jù)中進(jìn)行淘汰和在所有數(shù)據(jù)范圍內(nèi)進(jìn)行淘汰這兩類策略 )
在設(shè)置了過(guò)期時(shí)間的數(shù)據(jù)中進(jìn)行淘汰:
- volatile-random:隨機(jī)淘汰設(shè)置了過(guò)期時(shí)間的任意鍵值
- volatile-ttl:優(yōu)先淘汰更早過(guò)期的鍵值
- volatile-lru(Redis3.0 之前,默認(rèn)的內(nèi)存淘汰策略):淘汰所有設(shè)置了過(guò)期時(shí)間的鍵值中,最久未使用的鍵值
- volatile-lfu(Redis 4.0 后新增的內(nèi)存淘汰策略):淘汰所有設(shè)置了過(guò)期時(shí)間的鍵值中,最少使用的鍵值
在所有數(shù)據(jù)范圍內(nèi)進(jìn)行淘汰:
- allkeys-random:隨機(jī)淘汰任意鍵值
- allkeys-lru:淘汰整個(gè)鍵值中最久未使用的鍵值
- allkeys-lfu(Redis 4.0 后新增的內(nèi)存淘汰策略):淘汰整個(gè)鍵值中最少使用的鍵值
可以使用 config get maxmemory-policy 命令,來(lái)查看當(dāng)前 Redis 的內(nèi)存淘汰策略,命令如下:
127.0.0.1:6379> config get maxmemory-policy 1) "maxmemory-policy" 2) "noeviction"
Redis 使用的是 noeviction
類型的內(nèi)存淘汰策略,它是 Redis 3.0 之后默認(rèn)使用的內(nèi)存淘汰策略,表示當(dāng)運(yùn)行內(nèi)存超過(guò)最大設(shè)置內(nèi)存時(shí),不淘汰任何數(shù)據(jù),但新增操作會(huì)報(bào)錯(cuò)。
設(shè)置內(nèi)存淘汰策略有兩種方法:
- 方式一:通過(guò)config set maxmemory-policy <策略>命令設(shè)置。立即生效,不需要重啟 Redis 服務(wù),但重啟 Redis 之后,設(shè)置就會(huì)失效
- 方式二:通過(guò)修改 Redis 配置文件修改,設(shè)置maxmemory-policy <策略>,重啟 Redis 服務(wù)后配置不會(huì)丟失(修改了配置文件,必須重啟Redis服務(wù),設(shè)置才能生效)
LRU 算法和 LFU 算法有什么區(qū)別?
LRU全稱是 Least Recently Used 翻譯為 最近最少使用,會(huì)選擇淘汰最近最少使用的數(shù)據(jù)
Redis 并沒(méi)有使用這樣的方式實(shí)現(xiàn) LRU 算法,因?yàn)閭鹘y(tǒng)的 LRU 算法存在兩個(gè)問(wèn)題:
- 需要用鏈表管理所有的緩存數(shù)據(jù),這會(huì)帶來(lái)額外的空間開(kāi)銷;
- 當(dāng)有數(shù)據(jù)被訪問(wèn)時(shí),需要在鏈表上把該數(shù)據(jù)移動(dòng)到頭端,如果有大量數(shù)據(jù)被訪問(wèn),就會(huì)帶來(lái)很多鏈表移動(dòng)操作,會(huì)很耗時(shí),進(jìn)而會(huì)降低 Redis 緩存性能。
Redis 是如何實(shí)現(xiàn) LRU 算法的?
Redis 實(shí)現(xiàn)的是一種近似 LRU 算法,目的是為了更好的節(jié)約內(nèi)存,它的實(shí)現(xiàn)方式是在 Redis 的對(duì)象結(jié)構(gòu)體中添加一個(gè)額外的字段,用于記錄此數(shù)據(jù)的最后一次訪問(wèn)時(shí)間。
當(dāng) Redis 進(jìn)行內(nèi)存淘汰時(shí),會(huì)使用隨機(jī)采樣的方式來(lái)淘汰數(shù)據(jù),它是隨機(jī)取 5 個(gè)值(此值可配置),然后淘汰最久沒(méi)有使用的那個(gè)。
Redis 實(shí)現(xiàn)的 LRU 算法的優(yōu)點(diǎn):
- 不用為所有的數(shù)據(jù)維護(hù)一個(gè)大鏈表,節(jié)省了空間占用
- 不用在每次數(shù)據(jù)訪問(wèn)時(shí)都移動(dòng)鏈表項(xiàng),提升了緩存的性能
但是 LRU 算法有一個(gè)問(wèn)題,無(wú)法解決緩存污染問(wèn)題,比如應(yīng)用一次讀取了大量的數(shù)據(jù),而這些數(shù)據(jù)只會(huì)被讀取這一次,那么這些數(shù)據(jù)會(huì)留存在 Redis 緩存中很長(zhǎng)一段時(shí)間,造成緩存污染。因此,在 Redis 4.0 之后引入了 LFU 算法來(lái)解決這個(gè)問(wèn)題
什么是 LFU 算法?
LFU 全稱是 Least Frequently Used 翻譯為最近最不常用的,LFU 算法是根據(jù)數(shù)據(jù)訪問(wèn)次數(shù)來(lái)淘汰數(shù)據(jù)的,它的核心思想是"如果數(shù)據(jù)過(guò)去被訪問(wèn)多次,那么將來(lái)被訪問(wèn)的頻率也更高"。所以, LFU 算法會(huì)記錄每個(gè)數(shù)據(jù)的訪問(wèn)次數(shù)。當(dāng)一個(gè)數(shù)據(jù)被再次訪問(wèn)時(shí),就會(huì)增加該數(shù)據(jù)的訪問(wèn)次數(shù)。這樣就解決了偶爾被訪問(wèn)一次之后,數(shù)據(jù)留存在緩存中很長(zhǎng)一段時(shí)間的問(wèn)題,相比于 LRU 算法也更合理一些.
Redis 是如何實(shí)現(xiàn) LFU 算法的?
LFU 算法相比于 LRU 算法的實(shí)現(xiàn),多記錄了「數(shù)據(jù)的訪問(wèn)頻次」的信息。
Redis 對(duì)象的結(jié)構(gòu)如下:
typedef struct redisObject { ... unsigned lru:24; // 24 bits,用于記錄對(duì)象的訪問(wèn)信息 ... } robj;
Redis 對(duì)象頭中的 lru 字段,在 LRU 算法下和 LFU 算法下使用方式并不相同。
在 LRU 算法中,Redis 對(duì)象頭的 24 bits 的 lru 字段是用來(lái)記錄 key 的訪問(wèn)時(shí)間戳,因此在 LRU 模式下,Redis可以根據(jù)對(duì)象頭中的 lru 字段記錄的值,來(lái)比較最后一次 key 的訪問(wèn)時(shí)間長(zhǎng),從而淘汰最久未被使用的 key。
在 LFU 算法中,Redis對(duì)象頭的 24 bits 的 lru 字段被分成兩段來(lái)存儲(chǔ),高 16bit 存儲(chǔ) ldt(Last Decrement Time),低 8bit 存儲(chǔ) logc(Logistic Counter)。
- ldt 是用來(lái)記錄 key 的訪問(wèn)時(shí)間戳
- logc 是用來(lái)記錄 key 的訪問(wèn)頻次,它的值越小表示使用頻率越低,越容易淘汰,每個(gè)新加入的 key 的logc 初始值為 5。
注意:logc并不是單純的訪問(wèn)次數(shù),而是訪問(wèn)頻次(訪問(wèn)頻率),因?yàn)閘ogc會(huì)隨時(shí)間推移而衰減的。
在每次 key 被訪問(wèn)時(shí),會(huì)先對(duì) logc 做一個(gè)衰減操作,衰減的值跟前后訪問(wèn)時(shí)間的差距有關(guān)系,如果上一次訪問(wèn)的時(shí)間與這一次訪問(wèn)的時(shí)間差距很大,那么衰減的值就越大,這樣實(shí)現(xiàn)的 LFU 算法是根據(jù)訪問(wèn)頻率來(lái)淘汰數(shù)據(jù)的,而不只是訪問(wèn)次數(shù)。訪問(wèn)頻率需要考慮 key 的訪問(wèn)是多長(zhǎng)時(shí)間段內(nèi)發(fā)生的。key 的先前訪問(wèn)距離當(dāng)前時(shí)間越長(zhǎng),那么這個(gè) key 的訪問(wèn)頻率相應(yīng)地也就會(huì)降低,這樣被淘汰的概率也會(huì)更大。
對(duì) logc 做完衰減操作后,就開(kāi)始對(duì) logc 進(jìn)行增加操作,增加操作并不是單純的 + 1,而是根據(jù)概率增加,如果 logc 越大的 key,它的 logc 就越難再增加。
所以,Redis 在訪問(wèn) key 時(shí),對(duì)于 logc 是這樣變化的: 先按照上次訪問(wèn)距離當(dāng)前的時(shí)長(zhǎng),來(lái)對(duì) logc 進(jìn)行衰減; 然后,再按照一定概率增加 logc 的值
redis.conf 提供了兩個(gè)配置項(xiàng),用于調(diào)整 LFU 算法從而控制 logc 的增長(zhǎng)和衰減:
- lfu-decay-time:用于調(diào)整 logc 的衰減速度,它是一個(gè)以分鐘為單位的數(shù)值,默認(rèn)值為1,lfu-decay-time 值越大,衰減越慢;
- lfu-log-factor:用于調(diào)整 logc 的增長(zhǎng)速度,lfu-log-factor 值越大,logc 增長(zhǎng)越慢。
到此這篇關(guān)于Redis過(guò)期刪除策略與內(nèi)存淘汰策略的文章就介紹到這了,更多相關(guān)Redis刪除策略內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于Redis的分布式鎖的簡(jiǎn)單實(shí)現(xiàn)方法
這篇文章主要介紹了基于Redis的分布式鎖的簡(jiǎn)單實(shí)現(xiàn)方法,Redis官方給出兩種思路,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-10-10啟動(dòng)redis出現(xiàn)閃退情況的解決辦法
最近使用Redis遇到啟動(dòng)閃退的問(wèn)題,查閱資料后在一位大神的文章中找到了答案,這篇文章主要給大家介紹了關(guān)于啟動(dòng)redis出現(xiàn)閃退情況的解決辦法,需要的朋友可以參考下2023-11-11redis由于目標(biāo)計(jì)算機(jī)積極拒絕,無(wú)法連接的解決
這篇文章主要介紹了redis由于目標(biāo)計(jì)算機(jī)積極拒絕,無(wú)法連接的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07Redis報(bào)錯(cuò):Could not create server TCP 
這篇文章主要介紹了Redis報(bào)錯(cuò):Could not create server TCP listening socket 127.0.0.1:6379: bind:解決方法,是安裝與啟動(dòng)Redis過(guò)程中比較常見(jiàn)的問(wèn)題,需要的朋友可以參考下2023-06-06基于Redis驗(yàn)證碼發(fā)送及校驗(yàn)方案實(shí)現(xiàn)
本文主要介紹了基于Redis驗(yàn)證碼發(fā)送及校驗(yàn)方案實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01

redis過(guò)期回調(diào)功能實(shí)現(xiàn)示例

淺談Redis如何應(yīng)對(duì)并發(fā)訪問(wèn)