您好,登錄后才能下訂單哦!
這篇文章主要介紹“Redis內(nèi)存滿了然后去優(yōu)化”的相關(guān)知識(shí),小編通過(guò)實(shí)際案例向大家展示操作過(guò)程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“Redis內(nèi)存滿了然后去優(yōu)化”文章能幫助大家解決問(wèn)題。
redis內(nèi)存數(shù)據(jù)集大小上升到一定大小的時(shí)候,就會(huì)施行數(shù)據(jù)淘汰策略。
內(nèi)存。
如果達(dá)到設(shè)置的上限,Redis的寫命令會(huì)返回錯(cuò)誤信息(但是讀命令還可以正常返回。)或者你可以配置內(nèi)存淘汰機(jī)制,當(dāng)Redis達(dá)到內(nèi)存上限時(shí)會(huì)沖刷掉舊的內(nèi)容。
Redis 緩存有哪些淘汰策略?
不進(jìn)行數(shù)據(jù)淘汰的策略,只有 noeviction 這一種。
會(huì)進(jìn)行淘汰的 7 種策略,我們可以再進(jìn)一步根據(jù)淘汰候選數(shù)據(jù)集的范圍把它們分成兩類:
在設(shè)置了過(guò)期時(shí)間的數(shù)據(jù)中進(jìn)行淘汰,包括 volatile-random、volatile-ttl、volatile-lru、volatile-lfu四種。
在所有數(shù)據(jù)范圍內(nèi)進(jìn)行淘汰,包括 allkeys-lru、allkeys-random、allkeys-lfu三種。
策略 | 規(guī)則 |
---|---|
volatile-ttl | 在篩選時(shí),會(huì)針對(duì)設(shè)置了過(guò)期時(shí)間的鍵值對(duì),根據(jù)過(guò)期時(shí)間的先后進(jìn)行刪除,越早過(guò)期的越先被刪除。 |
volatile-random | 在設(shè)置了過(guò)期時(shí)間的鍵值對(duì)中,進(jìn)行隨機(jī)刪除。 |
volatile-lru | 使用 LRU 算法篩選設(shè)置了過(guò)期時(shí)間的鍵值對(duì) |
volatile-lfu | 使用 LFU 算法選擇設(shè)置了過(guò)期時(shí)間的鍵值對(duì) |
策略 | 規(guī)則 |
---|---|
allkeys-random | 從所有鍵值對(duì)中隨機(jī)選擇并刪除數(shù)據(jù); |
allkeys-lru | 使用 LRU 算法在所有數(shù)據(jù)中進(jìn)行篩選 |
vallkeys-lfu | 使用 LFU 算法在所有數(shù)據(jù)中進(jìn)行篩選 |
是按照最近最少使用的原則來(lái)篩選數(shù)據(jù),最不常用的數(shù)據(jù)會(huì)被篩選出來(lái),而最近頻繁使用的數(shù)據(jù)會(huì)留在緩存中。
那具體是怎么篩選的呢?LRU 會(huì)把所有的數(shù)據(jù)組織成一個(gè)鏈表,鏈表的頭和尾分別表示 MRU 端和 LRU 端,分別代表最近最常使用的數(shù)據(jù)和最近最不常用的數(shù)據(jù)。
LRU 算法背后的想法非常樸素:它認(rèn)為剛剛被訪問(wèn)的數(shù)據(jù),肯定還會(huì)被再次訪問(wèn),所以就把它放在 MRU 端;長(zhǎng)久不訪問(wèn)的數(shù)據(jù),肯定就不會(huì)再被訪問(wèn)了,所以就讓它逐漸后移到 LRU 端,在緩存滿時(shí),就優(yōu)先刪除它。
問(wèn)題:LRU 算法在實(shí)際實(shí)現(xiàn)時(shí),需要用鏈表管理所有的緩存數(shù)據(jù),這會(huì)帶來(lái)額外的空間開銷。而且,當(dāng)有數(shù)據(jù)被訪問(wèn)時(shí),需要在鏈表上把該數(shù)據(jù)移動(dòng)到 MRU 端,如果有大量數(shù)據(jù)被訪問(wèn),就會(huì)帶來(lái)很多鏈表移動(dòng)操作,會(huì)很耗時(shí),進(jìn)而會(huì)降低 Redis 緩存性能。
解決:
在 Redis 中,LRU 算法被做了簡(jiǎn)化,以減輕數(shù)據(jù)淘汰對(duì)緩存性能的影響。具體來(lái)說(shuō),Redis 默認(rèn)會(huì)記錄每個(gè)數(shù)據(jù)的最近一次訪問(wèn)的時(shí)間戳(由鍵值對(duì)數(shù)據(jù)結(jié)構(gòu) RedisObject 中的 lru 字段記錄)。然后,Redis 在決定淘汰的數(shù)據(jù)時(shí),第一次會(huì)隨機(jī)選出 N 個(gè)數(shù)據(jù),把它們作為一個(gè)候選集合。接下來(lái),Redis 會(huì)比較這 N 個(gè)數(shù)據(jù)的 lru 字段,把 lru 字段值最小的數(shù)據(jù)從緩存中淘汰出去。
當(dāng)需要再次淘汰數(shù)據(jù)時(shí),Redis 需要挑選數(shù)據(jù)進(jìn)入第一次淘汰時(shí)創(chuàng)建的候選集合。這兒的挑選標(biāo)準(zhǔn)是:能進(jìn)入候選集合的數(shù)據(jù)的 lru 字段值必須小于候選集合中最小的 lru 值。當(dāng)有新數(shù)據(jù)進(jìn)入候選數(shù)據(jù)集后,如果候選數(shù)據(jù)集中的數(shù)據(jù)個(gè)數(shù)達(dá)到了 maxmemory-samples,Redis 就把候選數(shù)據(jù)集中 lru 字段值最小的數(shù)據(jù)淘汰出去。
使用建議:
優(yōu)先使用 allkeys-lru 策略。這樣,可以充分利用 LRU 這一經(jīng)典緩存算法的優(yōu)勢(shì),把最近最常訪問(wèn)的數(shù)據(jù)留在緩存中,提升應(yīng)用的訪問(wèn)性能。如果你的業(yè)務(wù)數(shù)據(jù)中有明顯的冷熱數(shù)據(jù)區(qū)分,我建議你使用 allkeys-lru 策略。
如果業(yè)務(wù)應(yīng)用中的數(shù)據(jù)訪問(wèn)頻率相差不大,沒(méi)有明顯的冷熱數(shù)據(jù)區(qū)分,建議使用 allkeys-random 策略,隨機(jī)選擇淘汰的數(shù)據(jù)就行。
如果你的業(yè)務(wù)中有置頂?shù)男枨?,比如置頂新聞、置頂視頻,那么,可以使用 volatile-lru 策略,同時(shí)不給這些置頂數(shù)據(jù)設(shè)置過(guò)期時(shí)間。這樣一來(lái),這些需要置頂?shù)臄?shù)據(jù)一直不會(huì)被刪除,而其他數(shù)據(jù)會(huì)在過(guò)期時(shí)根據(jù) LRU 規(guī)則進(jìn)行篩選。
一旦被淘汰的數(shù)據(jù)選定后,如果這個(gè)數(shù)據(jù)是干凈數(shù)據(jù),那么我們就直接刪除;如果這個(gè)數(shù)據(jù)是臟數(shù)據(jù),我們需要把它寫回?cái)?shù)據(jù)庫(kù)。
那怎么判斷一個(gè)數(shù)據(jù)到底是干凈的還是臟的呢?
干凈數(shù)據(jù)和臟數(shù)據(jù)的區(qū)別就在于,和最初從后端數(shù)據(jù)庫(kù)里讀取時(shí)的值相比,有沒(méi)有被修改過(guò)。干凈數(shù)據(jù)一直沒(méi)有被修改,所以后端數(shù)據(jù)庫(kù)里的數(shù)據(jù)也是最新值。在替換時(shí),它可以被直接刪除。
而臟數(shù)據(jù)就是曾經(jīng)被修改過(guò)的,已經(jīng)和后端數(shù)據(jù)庫(kù)中保存的數(shù)據(jù)不一致了。此時(shí),如果不把臟數(shù)據(jù)寫回到數(shù)據(jù)庫(kù)中,這個(gè)數(shù)據(jù)的最新值就丟失了,就會(huì)影響應(yīng)用的正常使用。
即使淘汰的數(shù)據(jù)是臟數(shù)據(jù),Redis 也不會(huì)把它們寫回?cái)?shù)據(jù)庫(kù)。所以,我們?cè)谑褂?Redis 緩存時(shí),如果數(shù)據(jù)被修改了,需要在數(shù)據(jù)修改時(shí)就將它寫回?cái)?shù)據(jù)庫(kù)。否則,這個(gè)臟數(shù)據(jù)被淘汰時(shí),會(huì)被 Redis 刪除,而數(shù)據(jù)庫(kù)里也沒(méi)有最新的數(shù)據(jù)了。
1、控制key的數(shù)量:當(dāng)使用Redis存儲(chǔ)大量數(shù)據(jù)時(shí),通常會(huì)存在大量鍵,過(guò)多的鍵同樣會(huì)消耗大量?jī)?nèi)存。Redis本質(zhì)是一個(gè)數(shù)據(jù)結(jié)構(gòu)服務(wù)器,它為我們提供多種數(shù)據(jù)結(jié)構(gòu),如hash,list,set,zset 等結(jié)構(gòu)。使用Redis時(shí)不要進(jìn)入一個(gè)誤區(qū),大量使用get/set這樣的API,把Redis當(dāng)成Memcached使用。對(duì)于存儲(chǔ)相同的數(shù)據(jù)內(nèi)容利用Redis的數(shù)據(jù)結(jié)構(gòu)降低外層鍵的數(shù)量,也可以節(jié)省大量?jī)?nèi)存。
2、縮減鍵值對(duì)象,降低Redis內(nèi)存使用最直接的方式就是縮減鍵(key)和值(value)的長(zhǎng)度。
key長(zhǎng)度:如在設(shè)計(jì)鍵時(shí),在完整描述業(yè)務(wù)情況下,鍵值越短越好。
value長(zhǎng)度:值對(duì)象縮減比較復(fù)雜,常見需求是把業(yè)務(wù)對(duì)象序列化成二進(jìn)制數(shù)組放入Redis。首先應(yīng)該在業(yè)務(wù)上精簡(jiǎn)業(yè)務(wù)對(duì)象,去掉不必要的屬性避免存儲(chǔ)無(wú)效數(shù)據(jù)。其次在序列化工具選擇上,應(yīng)該選擇更高效的序列化工具來(lái)降低字節(jié)數(shù)組大小。
3、編碼優(yōu)化。Redis對(duì)外提供了string,list,hash,set,zet等類型,但是Redis內(nèi)部針對(duì)不同類型存在編碼的概念,所謂編碼就是具體使用哪種底層數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)。編碼不同將直接影響數(shù)據(jù)的內(nèi)存占用和讀寫效率。
1、redisObject對(duì)象
type字段:
利用集合類型數(shù)據(jù),因?yàn)橥ǔG闆r下很多小的Key-Value可以用更緊湊的方式存放到一起。盡可能使用散列表(hashes),散列表(是說(shuō)散列表里面存儲(chǔ)的數(shù)少)使用的內(nèi)存非常小,所以你應(yīng)該盡可能的將你的數(shù)據(jù)模型抽象到一個(gè)散列表里面。比如你的web系統(tǒng)中有一個(gè)用戶對(duì)象,不要為這個(gè)用戶的名稱,姓氏,郵箱,密碼設(shè)置單獨(dú)的key,而是應(yīng)該把這個(gè)用戶的所有信息存儲(chǔ)到一張散列表里面。
encoding字段:
采用不同的編碼實(shí)現(xiàn)內(nèi)存占用存在明顯差異
lru字段:
開發(fā)提示:可以使用scan + object idletime 命令批量查詢哪些鍵長(zhǎng)時(shí)間未被訪問(wèn),找出長(zhǎng)時(shí)間不訪問(wèn)的鍵進(jìn)行清理降低內(nèi)存占用。
refcount字段:
當(dāng)對(duì)象為整數(shù)且范圍在[0-9999]時(shí),Redis可以使用共享對(duì)象的方式來(lái)節(jié)省內(nèi)存。
ptr字段 :
開發(fā)提示:高并發(fā)寫入場(chǎng)景中,在條件允許的情況下建議字符串長(zhǎng)度控制在39字節(jié)以內(nèi),減少創(chuàng)建redisObject內(nèi)存分配次數(shù)從而提高性能。
2、縮減鍵值對(duì)象
降低Redis內(nèi)存使用最直接的方式就是縮減鍵(key)和值(value)的長(zhǎng)度。
可以使用通用壓縮算法壓縮json,xml后再存入Redis,從而降低內(nèi)存占用
3、共享對(duì)象池
對(duì)象共享池指Redis內(nèi)部維護(hù)[0-9999]的整數(shù)對(duì)象池。創(chuàng)建大量的整數(shù)類型redisObject存在內(nèi)存開銷,每個(gè)redisObject內(nèi)部結(jié)構(gòu)至少占16字節(jié),甚至超過(guò)了整數(shù)自身空間消耗。所以Redis內(nèi)存維護(hù)一個(gè)[0-9999]的整數(shù)對(duì)象池,用于節(jié)約內(nèi)存。 除了整數(shù)值對(duì)象,其他類型如list,hash,set,zset內(nèi)部元素也可以使用整數(shù)對(duì)象池。因此開發(fā)中在滿足需求的前提下,盡量使用整數(shù)對(duì)象以節(jié)省內(nèi)存。
當(dāng)設(shè)置maxmemory并啟用LRU相關(guān)淘汰策略如:volatile-lru,allkeys-lru時(shí),Redis禁止使用共享對(duì)象池。
為什么開啟maxmemory和LRU淘汰策略后對(duì)象池?zé)o效?
LRU算法需要獲取對(duì)象最后被訪問(wèn)時(shí)間,以便淘汰最長(zhǎng)未訪問(wèn)數(shù)據(jù),每個(gè)對(duì)象最后訪問(wèn)時(shí)間存儲(chǔ)在redisObject對(duì)象的lru字段。對(duì)象共享意味著多個(gè)引用共享同一個(gè)redisObject,這時(shí)lru字段也會(huì)被共享,導(dǎo)致無(wú)法獲取每個(gè)對(duì)象的最后訪問(wèn)時(shí)間。如果沒(méi)有設(shè)置maxmemory,直到內(nèi)存被用盡Redis也不會(huì)觸發(fā)內(nèi)存回收,所以共享對(duì)象池可以正常工作。
綜上所述,共享對(duì)象池與maxmemory+LRU策略沖突,使用時(shí)需要注意。
為什么只有整數(shù)對(duì)象池?
首先整數(shù)對(duì)象池復(fù)用的幾率最大,其次對(duì)象共享的一個(gè)關(guān)鍵操作就是判斷相等性,Redis之所以只有整數(shù)對(duì)象池,是因?yàn)檎麛?shù)比較算法時(shí)間復(fù)雜度為O(1),只保留一萬(wàn)個(gè)整數(shù)為了防止對(duì)象池浪費(fèi)。如果是字符串判斷相等性,時(shí)間復(fù)雜度變?yōu)镺(n),特別是長(zhǎng)字符串更消耗性能(浮點(diǎn)數(shù)在Redis內(nèi)部使用字符串存儲(chǔ))。對(duì)于更復(fù)雜的數(shù)據(jù)結(jié)構(gòu)如hash,list等,相等性判斷需要O(n2)。對(duì)于單線程的Redis來(lái)說(shuō),這樣的開銷顯然不合理,因此Redis只保留整數(shù)共享對(duì)象池。
4、字符串優(yōu)化
Redis沒(méi)有采用原生C語(yǔ)言的字符串類型而是自己實(shí)現(xiàn)了字符串結(jié)構(gòu),內(nèi)部簡(jiǎn)單動(dòng)態(tài)字符串,簡(jiǎn)稱SDS。
字符串結(jié)構(gòu):
特點(diǎn):
O(1)時(shí)間復(fù)雜度獲?。鹤址L(zhǎng)度,已用長(zhǎng)度,未用長(zhǎng)度。
可用于保存字節(jié)數(shù)組,支持安全的二進(jìn)制數(shù)據(jù)存儲(chǔ)。
內(nèi)部實(shí)現(xiàn)空間預(yù)分配機(jī)制,降低內(nèi)存再分配次數(shù)。
惰性刪除機(jī)制,字符串縮減后的空間不釋放,作為預(yù)分配空間保留。
預(yù)分配機(jī)制:
開發(fā)提示:盡量減少字符串頻繁修改操作如append,setrange, 改為直接使用set修改字符串,降低預(yù)分配帶來(lái)的內(nèi)存浪費(fèi)和內(nèi)存碎片化。
字符串重構(gòu):基于hash類型的二級(jí)編碼方式。
二級(jí)編碼怎么用?
二級(jí)編碼方法中采用的 ID 長(zhǎng)度是有講究的。
涉及到一個(gè)問(wèn)題–Hash 類型底層結(jié)構(gòu)小于設(shè)定值時(shí)使用壓縮列表,大于設(shè)定值時(shí)使用哈希表。
一旦從壓縮列表轉(zhuǎn)為了哈希表,Hash 類型會(huì)一直用哈希表進(jìn)行保存,而不會(huì)再轉(zhuǎn)回壓縮列表。
在節(jié)省內(nèi)存空間方面,哈希表就沒(méi)有壓縮列表那么高效。為能充分使用壓縮列表的精簡(jiǎn)內(nèi)存布局,一般要控制保存在 Hash 中的元素個(gè)數(shù)。
5.編碼優(yōu)化
使用壓縮列表ziplist編碼的hash類型依然比使用hashtable編碼的集合節(jié)省大量?jī)?nèi)存。
6.控制key的數(shù)量
開發(fā)提示:使用ziplist+hash優(yōu)化keys后,如果想使用超時(shí)刪除功能,開發(fā)人員可以存儲(chǔ)每個(gè)對(duì)象寫入的時(shí)間,再通過(guò)定時(shí)任務(wù)使用hscan命令掃描數(shù)據(jù),找出hash內(nèi)超時(shí)的數(shù)據(jù)項(xiàng)刪除即可。
當(dāng)Redis內(nèi)存不足時(shí),首先考慮的問(wèn)題不是加機(jī)器做水平擴(kuò)展,應(yīng)該先嘗試做內(nèi)存優(yōu)化。當(dāng)遇到瓶頸時(shí),再去考慮水平擴(kuò)展。即使對(duì)于集群化方案,垂直層面優(yōu)化也同樣重要,避免不必要的資源浪費(fèi)和集群化后的管理成本。
關(guān)于“Redis內(nèi)存滿了然后去優(yōu)化”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。