Redis大Key問題的解決方案
什么是大Key問題
Redis中某些鍵(key)所對應(yīng)的值(value)特別大,或者集合類數(shù)據(jù)結(jié)構(gòu)(如hash、set、zset、list)中存儲的元素數(shù)量過多,這就是大Key問題。它分為以下三種類型:
單個字符串類型(String)Key的Value特別大
具體大小標(biāo)準(zhǔn)依據(jù)業(yè)務(wù)場景不同而有所變化,一般認(rèn)為在普通業(yè)務(wù)場景下,如果單個String類型的value大于1MB,或者在高并發(fā)低延遲場景中大于10KB,就可能被視為大Key。
集合數(shù)據(jù)類型(如Hash、Set、ZSet、List)中的元素數(shù)量過多或總體數(shù)據(jù)量過大
例如,一個Hash類型Key的成員數(shù)量雖只有1000個,但這些成員的Value總大小達(dá)到100MB,或者一個ZSet類型的Key成員數(shù)量達(dá)到10000個,也會被看作是大Key問題。
單個Key的內(nèi)存占用過高
比如阿里云Redis定義中,一個String類型的Key其值達(dá)到5MB,或一個ZSet類型的Key成員數(shù)量達(dá)到10000個,都被視為大Key。
負(fù)面影響
讀取成本高
大Key由于體積大,讀取時會消耗更多的時間,增加延遲,尤其是在網(wǎng)絡(luò)傳輸中,大Key會占用更多的帶寬,影響系統(tǒng)的響應(yīng)速度和網(wǎng)絡(luò)資源的有效利用。
寫操作易阻塞
寫入大Key時,由于Redis采用單線程模型處理請求,操作大Key會阻塞其他命令的執(zhí)行,導(dǎo)致整個Redis服務(wù)響應(yīng)變慢,甚至無法正常響應(yīng)其他請求。
慢查詢與主從同步異常
大Key的讀寫操作時間長,可能觸發(fā)Redis的慢查詢?nèi)罩居涗洠l繁的慢查詢會加重服務(wù)器負(fù)擔(dān)。同時,在主從復(fù)制場景下,大Key的同步也會比小Key慢,可能影響數(shù)據(jù)的一致性和實時性。
占用更多存儲空間,導(dǎo)致逐出與OOM
大Key占據(jù)大量內(nèi)存空間,容易觸發(fā)Redis的內(nèi)存淘汰策略,造成重要數(shù)據(jù)被意外移除(逐出)。在極端情況下,可能會導(dǎo)致Redis實例因內(nèi)存耗盡而崩潰(OOM)。
集群架構(gòu)下的內(nèi)存資源不均衡
在Redis集群中,若某個分片上有大Key,該分片的內(nèi)存使用率將遠(yuǎn)高于其他分片,打破集群間內(nèi)存使用的均衡狀態(tài),影響集群的整體性能和穩(wěn)定性。
影響高并發(fā)與低延遲要求的場景
在對時延敏感的應(yīng)用中,大Key的存在會顯著增加請求處理時間,降低系統(tǒng)處理高并發(fā)請求的能力。
產(chǎn)生原因
業(yè)務(wù)設(shè)計不合理
最常見的原因是在沒有合理拆分的情況下,直接將大量數(shù)據(jù)(如大的JSON對象或二進(jìn)制文件數(shù)據(jù))存儲在一個鍵中。這種做法忽視了Redis作為內(nèi)存數(shù)據(jù)庫的特性,沒有充分利用其高效處理小數(shù)據(jù)塊的優(yōu)勢。
未能處理Value動態(tài)增長問題
隨著時間推移,如果持續(xù)向某個鍵的Value中添加數(shù)據(jù),而又沒有相應(yīng)的定期刪除機(jī)制、合理的過期策略或大小限制,Value的大小最終會增長到難以管理的程度。例如,不斷累積的微博粉絲列表、熱門評論或直播彈幕等場景很容易形成大Key。
程序Bug
有時候,軟件開發(fā)中的錯誤可能導(dǎo)致某些鍵的生命周期超出預(yù)期,或者其包含的元素數(shù)量異常增長。例如,如果負(fù)責(zé)消費(fèi)LIST類型鍵的業(yè)務(wù)代碼發(fā)生故障,可能會導(dǎo)致該Key的成員只增不減,進(jìn)而形成大Key。
找出大Key
使用redis-cli的--bigkeys
參數(shù)
這是最直接的方法。通過Redis命令行工具redis-cli,使用--bigkeys
參數(shù)來掃描Redis實例中的所有Key。它會遍歷整個鍵空間并返回每個數(shù)據(jù)類型中最大的Key的信息。執(zhí)行命令如下:
redis-cli --bigkeys
此命令會輸出每個數(shù)據(jù)類型中最大的Key及其相關(guān)信息,以及一些整體統(tǒng)計信息,如不同類型Key的數(shù)量、平均長度等。
利用Redis RDB Tools
一種更深入和定制化的分析方法是使用Redis RDB Tools這個開源工具。首先,導(dǎo)出Redis的RDB文件,然后使用該工具分析此文件,找出大Key。例如,輸出占用內(nèi)存超過128字節(jié)的前5個Keys到CSV文件:
rdb -c memory dump.rdb --bytes 128 --largest 5 -f memory.csv
這樣做可以更精確地控制分析條件,比如按大小過濾Key,或者按數(shù)量篩選最大的幾個Key。
可觀測性分析
通過監(jiān)控工具,跟蹤Redis的性能指標(biāo),如延遲、吞吐量和錯誤率,以及分析慢查詢?nèi)罩?,也是發(fā)現(xiàn)大Key的一種間接方法。如果存在執(zhí)行時間過長的操作,這可能是大Key導(dǎo)致的。部分云服務(wù)提供商還可能提供了直接查看Top Key統(tǒng)計的功能,便于發(fā)現(xiàn)內(nèi)存占用高的Key。
解決方案
那么通過以上手段找出大Key后,就需要分析兩個問題??:
- 確定大Key是否是業(yè)務(wù)必要的(如果不是,就讓它棍??),能不能通過業(yè)務(wù)邏輯的優(yōu)化來處理這個問題
- 檢查程序Bug,看看是不是代碼寫的有問題??,導(dǎo)致Key的大小不正常
如果以上問題都檢查無誤,也沒有解決大Key問題,可以考慮下面的優(yōu)化策略:
?? 避免大Key問題
解決問題的最好辦法就是解決提出問題的人
在業(yè)務(wù)設(shè)計的初期就應(yīng)該避免生成大Key,僅僅緩存必要的數(shù)據(jù)字段。
? 數(shù)據(jù)拆分
就像視頻分片緩沖一樣,可以考慮把大Key分片成小Key進(jìn)行存儲。比如直接存儲中國的所有省市區(qū)的一些詳細(xì)信息(招商、氣象)肯定會產(chǎn)生大Key(“China”),但如果分片存儲,定義一個namespace,然后省、市、區(qū)分別去存,就形成了小key,具體說明就是:
定義namespace:可以將“China”作為一個命名空間。
省、市、區(qū)分別存儲:
- 省級信息可以存儲為
China:province:省份ID
,值為該省的省級詳細(xì)信息。 - 市級信息存儲為
China:city:城市ID
,值為該市的市級詳細(xì)信息。 - 區(qū)縣級信息存儲為
China:district:區(qū)域ID
,值為該區(qū)的區(qū)縣級詳細(xì)信息。
- 省級信息可以存儲為
這種是靠定量可以解決的。如果是不定量的需求,即Value會增長的,那么就可以考慮依據(jù)第一個分片Value有幾片,再按照這個num往下分。
不過,分片可能會造成部分寫的問題。比如,一個不恰當(dāng)?shù)美斫獾睦印S脩籼峤挥唵螘r,需要同時寫入這三個分片:
Order:Info:{OrderID}
-> 訂單基本信息Order:Items:{OrderID}
-> 訂單商品列表Order:Payment:{OrderID}
-> 訂單支付信息
假設(shè)在執(zhí)行寫操作時,發(fā)生以下情況:
Order:Info:{OrderID}
寫入成功Order:Items:{OrderID}
寫入成功Order:Payment:{OrderID}
寫入失敗
這就是部分寫問題。那么在設(shè)計階段,可以為每一個Value加入一個版本號,以進(jìn)行一致性檢查,如果有一個版本號沒對上,就立馬回源,重新讀取,然后重新加載并再次嘗試寫入。
?? 換個方向
你有沒有考慮過這個數(shù)據(jù)就不應(yīng)該用String存儲?
比如在網(wǎng)絡(luò)爬蟲中,開發(fā)人員可能會用一個很長的String來記錄哪些URL已經(jīng)被訪問過,每個字符對應(yīng)一個URL的hash值,但這樣會浪費(fèi)內(nèi)存空間。如果用Bitmap來記錄訪問過的URL,每一位表示一個URL的hash值,這樣可以節(jié)省大量內(nèi)存,并且能快速判斷URL是否已經(jīng)訪問過。
又或者是用戶活躍度統(tǒng)計。如果要記錄一個大型網(wǎng)站每天數(shù)百萬用戶的登錄活躍情況,對于每一天,可以用一個整數(shù)的32位(或64位)比特位來表示當(dāng)天所有用戶是否登錄過,其中每位代表一個用戶ID是否活躍。如果使用String來存儲,即使每個用戶活躍狀態(tài)只需一位表示,也會因為String的編碼方式(如UTF-8)而占用更多的空間,每個字符至少占用8位。
?? 合理清理
只需要設(shè)計個方案合理清理就能避免大Key的累積。
- 在低峰期刪除。可以考慮在業(yè)務(wù)流量低的時候定時清理緩存。但很明顯,這個方案不太合理。假如一整天業(yè)務(wù)流量都很高,這時候已經(jīng)產(chǎn)生大Key了呢?
- 分批定時定量刪除。定量刪除是為了防止阻塞。
- 異步刪除。與del命令不同的是,unlink命令會異步地刪除指定的鍵以及與之相關(guān)聯(lián)的值。 即,它會將要刪除的鍵添加到一個待刪除的列表中,并立即返回,不會阻塞客戶端。 Redis服務(wù)器會在后臺異步地刪除待刪除列表中的鍵。
到此這篇關(guān)于Redis大Key問題的解決方案的文章就介紹到這了,更多相關(guān)redis大key內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解析Redis未授權(quán)訪問漏洞復(fù)現(xiàn)與利用危害
這篇文章主要介紹了Redis未授權(quán)訪問漏洞復(fù)現(xiàn)與利用,介紹了redis未授權(quán)訪問漏洞的基本概念及漏洞的危害,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-01-01淺談RedisTemplate和StringRedisTemplate的區(qū)別
本文主要介紹了RedisTemplate和StringRedisTemplate的區(qū)別及個人見解,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-06-06Redis內(nèi)存碎片率調(diào)優(yōu)處理方式
Redis集群因內(nèi)存碎片率超過1.5觸發(fā)告警,分析發(fā)現(xiàn)內(nèi)因與外因?qū)е聝?nèi)存碎片,內(nèi)因為操作系統(tǒng)內(nèi)存分配機(jī)制,外因為Redis操作特性,使用Redis內(nèi)置內(nèi)存碎片清理機(jī)制可有效降低碎片率,但需注意可能影響性能,建議使用MEMORY命令診斷內(nèi)存使用情況,合理配置參數(shù)以優(yōu)化性能2024-09-09