redis大key和大value的危害及解決
一、前序
還記得上次和同事一起去面試候選人時,同事提了一個問題:Redis的大key有什么危害?當時候選人主要作答的角度是一個key的value較大時的情況,比如:
- 內存不均:單value較大時,可能會導致節(jié)點之間的內存使用不均勻,間接地影響key的部分和負載不均勻;
- 阻塞請求:redis為單線程,單value較大讀寫需要較長的處理時間,會阻塞后續(xù)的請求處理;
- 阻塞網(wǎng)絡:單value較大時會占用服務器網(wǎng)卡較多帶寬,可能會影響該服務器上的其他Redis實例或者應用。
雖說答的是挺好的,但是我又隨之產(chǎn)生了另一個疑惑,如果redis的key較長時,會產(chǎn)生什么樣的影響呢?查了很多文章,說的都不是特別清楚。所以我決心探究一下這個問題。
我們需要知道Redis是如何存儲key和value的:
根結構為RedisServer,其中包含RedisDB(數(shù)據(jù)庫)。而RedisDB實際上是使用Dict(字典)結構對Redis中的kv進行存儲的。這里的key即字符串,value可以是string/hash/list/set/zset這五種對象之一。

Dict字典結構中,存儲數(shù)據(jù)的主題為DictHt,即哈希表。而哈希表本質上是一個DictEntry(哈希表節(jié)點)的數(shù)組,并且使用鏈表法解決哈希沖突問題(關于哈希沖突的解決方法可以參考大佬的文章 解決哈希沖突的常用方法分析)。
所以在這里實際存儲時,key和value都是存儲在DictEntry中的。所以基本上來說,大key和大value帶來的內存不均和網(wǎng)絡IO壓力都是一致的,只是key相較于value還多一個做hashcode和比較的過程(鏈表中進行遍歷比較key),會有更多的內存相關開銷。
二、什么是Redis大key問題
Redis大key問題指的是某個key對應的value值所占的內存空間比較大,導致Redis的性能下降、內存不足、數(shù)據(jù)不均衡以及主從同步延遲等問題。
到底多大的數(shù)據(jù)量才算是大key?
沒有固定的判別標準,通常認為字符串類型的key對應的value值占用空間大于1M,或者集合類型的k元素數(shù)量超過1萬個,就算是大key。
Redis大key問題的定義及評判準則并非一成不變,而應根據(jù)Redis的實際運用以及業(yè)務需求來綜合評估。例如,在高并發(fā)且低延遲的場景中,僅10kb可能就已構成大key;然而在低并發(fā)、高容量的環(huán)境下,大key的界限可能在100kb。因此,在設計與運用Redis時,要依據(jù)業(yè)務需求與性能指標來確立合理的大key閾值。
三、大key帶來的影響
- 內存占用過高。大Key占用過多的內存空間,可能導致可用內存不足,從而觸發(fā)內存淘汰策略。在極端情況下,可能導致內存耗盡,Redis實例崩潰,影響系統(tǒng)的穩(wěn)定性。
- 性能下降。大Key會占用大量內存空間,導致內存碎片增加,進而影響Redis的性能。對于大Key的操作,如讀取、寫入、刪除等,都會消耗更多的CPU時間和內存資源,進一步降低系統(tǒng)性能。
- 阻塞其他操作。某些對大Key的操作可能會導致Redis實例阻塞。例如,使用DEL命令刪除一個大Key時,可能會導致Redis實例在一段時間內無法響應其他客戶端請求,從而影響系統(tǒng)的響應時間和吞吐量。
- 網(wǎng)絡擁塞。每次獲取大key產(chǎn)生的網(wǎng)絡流量較大,可能造成機器或局域網(wǎng)的帶寬被打滿,同時波及其他服務。例如:一個大key占用空間是1MB,每秒訪問1000次,就有1000MB的流量。
- 主從同步延遲。當Redis實例配置了主從同步時,大Key可能導致主從同步延遲。由于大Key占用較多內存,同步過程中需要傳輸大量數(shù)據(jù),這會導致主從之間的網(wǎng)絡傳輸延遲增加,進而影響數(shù)據(jù)一致性。
- 數(shù)據(jù)傾斜。在Redis集群模式中,某個數(shù)據(jù)分片的內存使用率遠超其他數(shù)據(jù)分片,無法使數(shù)據(jù)分片的內存資源達到均衡。另外也可能造成Redis內存達到maxmemory參數(shù)定義的上限導致重要的key被逐出,甚至引發(fā)內存溢出。
四、大key產(chǎn)生的原因
- 業(yè)務設計不合理。這是最常見的原因,不應該把大量數(shù)據(jù)存儲在一個key中,而應該分散到多個key。例如:把全國數(shù)據(jù)按照省行政區(qū)拆分成34個key,或者按照城市拆分成300個key,可以進一步降低產(chǎn)生大key的概率。
- 沒有預見value的動態(tài)增長問題。如果一直添加value數(shù)據(jù),沒有刪除機制、過期機制或者限制數(shù)量,遲早出現(xiàn)大key。例如:微博明星的粉絲列表、熱門評論等。
- 過期時間設置不當。如果沒有給某個key設置過期時間,或者過期時間設置較長。隨著時間推移,value數(shù)量快速累積,最終形成大key。
- 程序bug。某些異常情況導致某些key的生命周期超出預期,或者value數(shù)量異常增長 ,也會產(chǎn)生大key。
五、怎樣排查大key
SCAN命令
通過使用Redis的SCAN命令,我們可以逐步遍歷數(shù)據(jù)庫中的所有Key。結合其他命令(如STRLEN、LLEN、SCARD、HLEN等),我們可以識別出大Key。SCAN命令的優(yōu)勢在于它可以在不阻塞Redis實例的情況下進行遍歷。
bigkeys參數(shù)
使用redis-cli命令客戶端,連接Redis服務的時候,加上 —bigkeys 參數(shù),可以掃描每種數(shù)據(jù)類型數(shù)量最大的key。
redis-cli -h 127.0.0.1 -p 6379 —bigkeys
Redis RDB Tools工具
使用開源工具Redis RDB Tools,分析RDB文件,掃描出Redis大key。
例如:輸出占用內存大于1kb,排名前3的keys。
rdb —commond memory —bytes 1024 —largest 3 dump.rbd
六、怎么解決大key
拆分成多個小key。這是最容易想到的辦法,降低單key的大小,讀取可以用mget批量讀取。
數(shù)據(jù)壓縮。使用String類型的時候,使用壓縮算法減少value大小。或者是使用Hash類型存儲,因為Hash類型底層使用了壓縮列表數(shù)據(jù)結構。
設置合理的過期時間。為每個key設置過期時間,并設置合理的過期時間,以便在數(shù)據(jù)失效后自動清理,避免長時間累積的大Key問題。
啟用內存淘汰策略。啟用Redis的內存淘汰策略,例如LRU(Least Recently Used,最近最少使用),以便在內存不足時自動淘汰最近最少使用的數(shù)據(jù),防止大Key長時間占用內存。
數(shù)據(jù)分片。例如使用Redis Cluster將數(shù)據(jù)分散到多個Redis實例,以減輕單個實例的負擔,降低大Key問題的風險。
刪除大key。使用UNLINK命令刪除大key,UNLINK命令是DEL命令的異步版本,它可以在后臺刪除Key,避免阻塞Redis實例。
七、Redis 大key如何處理?
Redis使用過程中經(jīng)常會有各種大key的情況, 比如:
單個簡單的key存儲的value很大
hash, set,zset,list 中存儲過多的元素(以萬為單位)
由于redis是單線程運行的,如果一次操作的value很大會對整個redis的響應時間造成負面影響,所以,業(yè)務上能拆則拆,下面舉幾個典型的分拆方案。
業(yè)務場景:
即通過hash的方式來存儲每一天用戶訂單次數(shù)。那么key = order_20200102, field = order_id, value = 10。那么如果一天有百萬千萬甚至上億訂單的時候,key后面的值是很多,存儲空間也很大,造成所謂的大key。
大key的風險:
讀寫大key會導致超時嚴重,甚至阻塞服務。
如果刪除大key,DEL命令可能阻塞Redis進程數(shù)十秒,使得其他請求阻塞,對應用程序和Redis集群可用性造成嚴重的影響。
redis使用會出現(xiàn)大key的場景:
- 單個簡單key的存儲的value過大;
- hash、set、zset、list中存儲過多的元素。
解決問題:
- 單個簡單key的存儲的value過大的解決方案:
將大key拆分成對個key-value,使用multiGet方法獲得值,這樣的拆分主要是為了減少單臺操作的壓力,而是將壓力平攤到集群各個實例中,降低單臺機器的IO操作。
- hash、set、zset、list中存儲過多的元素的解決方案:
1).類似于第一種場景,使用第一種方案拆分;
2).以hash為例,將原先的hget、hset方法改成(加入固定一個hash桶的數(shù)量為10000),先計算field的hash值模取10000,確定該field在哪一個key上。
將大key進行分割,為了均勻分割,可以對field進行hash并通過質數(shù)N取余,將余數(shù)加到key上面,我們取質數(shù)N為997。
那么新的key則可以設置為:
newKey = order_20200102_String.valueOf( Math.abs(order_id.hashcode() % 997) ) field = order_id value = 10 hset (newKey, field, value) ; hget(newKey, field)
八、 大value數(shù)據(jù)是什么,會有怎樣的問題?
當String類型的數(shù)據(jù)>10K,list、hash、set、sort set中元素個數(shù)超過1000時就可以被稱為大value,當超過100K,或集合元素個數(shù)超過10000時可以被稱為是超大value。大value最直接的影響就是有可能造成機器內存不足,就是數(shù)據(jù)傾斜;同時因為redis數(shù)據(jù)處理是單線程的,當value過大時,處理起來響應時間也會變慢。 常見的例子有:參與人數(shù)很多的蓋樓活動或者很活躍的群聊消息列表等
九、怎么處理Redis大value?
大value的處理方式還是結合業(yè)務,對其進行拆分,將其數(shù)據(jù)分布在各個redis節(jié)點中,將操作壓力平攤開,防止對單個實例IO或內存影響過大。
簡單說一下 熱點數(shù)據(jù)和大value的拆分,如果它是一個list、 set集合類型,比如原來的 為key value,value為list為拆為 list1 、list2、list3,那么新的key為 key+hash(list1)%10000 得到新的key,再對對應數(shù)據(jù)value進行set或get操作
如果是一個對象的json字符串,可以考慮將該對象的不同屬性映射到不同hash槽從而分布在不同redis節(jié)點中;或者將不同屬性拆分,利用hash結構進行存儲,從而每次處理時僅獲取一部分數(shù)據(jù)
十、總結
大key和大value的危害是一致的:內存不均、阻塞請求、阻塞網(wǎng)絡。
key由于比value需要做更多的操作如hashcode、鏈表中比較等操作,所以會比value更多一些內存相關開銷。
本文主要詳細介紹了大Key產(chǎn)生的原因、影響、檢測方法和解決方案。通過優(yōu)化數(shù)據(jù)結構設計、設定合理的數(shù)據(jù)過期策略、優(yōu)化系統(tǒng)架構和配置,以及漸進式刪除大Key等方法,我們可以有效地解決和預防大Key問題,從而提高Redis系統(tǒng)的穩(wěn)定性和性能。
到此這篇關于redis大key和大value的危害及解決的文章就介紹到這了,更多相關redis大key和大value內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
詳解redis腳本命令執(zhí)行問題(redis.call)
這篇文章主要介紹了redis腳本命令執(zhí)行問題(redis.call),分別介紹了redis-cli命令行中執(zhí)行及l(fā)inux命令行中執(zhí)行問題,本文給大家介紹的非常詳細,需要的朋友參考下吧2022-03-03
分布式鎖為什么要選擇Zookeeper而不是Redis?看完這篇你就明白了
Zookeeper的機制可以保證分布式鎖實現(xiàn)業(yè)務代碼簡單,成本低,Redis如果要解決分布式鎖的問題,對于一些復雜的情況,很難解決,成本較高,這篇文章重點給大家介紹分布式鎖選擇Zookeeper 而不是Redis的理由,一起看看吧2021-05-05
一文詳解Redis在Ubuntu系統(tǒng)上的安裝步驟
安裝redis在Ubuntu上有多種方法,下面這篇文章主要給大家介紹了關于Redis在Ubuntu系統(tǒng)上安裝的相關資料,文中通過圖文以及代碼介紹的非常詳細,需要的朋友可以參考下2024-07-07
基于 Redis 的 JWT令牌失效處理方案(實現(xiàn)步驟)
當用戶登錄狀態(tài)到登出狀態(tài)時,對應的JWT的令牌需要設置為失效狀態(tài),這時可以使用基于Redis 的黑名單方案來實現(xiàn)JWT令牌失效,本文給大家分享基于 Redis 的 JWT令牌失效處理方案,感興趣的朋友一起看看吧2024-03-03
redis在Linux系統(tǒng)下的環(huán)境配置和redis的全局命令大全
在Linux系統(tǒng)中我們經(jīng)常使用Redis作為高性能的緩存數(shù)據(jù)庫,然而有時候我們需要在系統(tǒng)中多個地方使用Redis命令,這就需要將Redis的全局命令設置好,這篇文章主要給大家介紹了關于redis在Linux系統(tǒng)下的環(huán)境配置和redis的全局命令大全的相關資料,需要的朋友可以參考下2024-05-05

