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

