Redis大key 問題分析解決

背景
雙十一大促期間, 收到客服反饋通知,說 APP 領(lǐng)券接口緩慢。找到一個(gè)case,通過調(diào)用鏈路發(fā)現(xiàn),是操作redis 緩慢,并且還搜到一些redis 異常。

最后定位到原因:是發(fā)券場景下拿redis 做了一個(gè)緩存券批次的操作,記錄用戶當(dāng)天領(lǐng)取的所有券批次
發(fā)券場景: key = userId, value = 券批次ID 列表, 而redis 查詢發(fā)現(xiàn)多了許多大key,體現(xiàn)在 一個(gè)用戶領(lǐng)取的幾千甚至上萬張優(yōu)惠券,導(dǎo)致Redis 查詢緩慢,甚至異常。至于為何有的用戶會(huì)領(lǐng)取這么多優(yōu)惠券呢。聯(lián)系風(fēng)控發(fā)現(xiàn),這些個(gè)用戶是來薅羊毛的,但是風(fēng)控沒有攔截到,導(dǎo)致服務(wù)這邊出現(xiàn)異常。

雖說最終原因不是我的問題,但是Redis 大 key 問題還是比較有意思的,下面我們就來一起認(rèn)識(shí)下 大 key 問題對 Redis 的影響。

什么是大key
所謂的大key問題是某個(gè)key的value比較大,所以本質(zhì)上是大value問題。key往往是程序可以自行設(shè)置的,value往往不受程序控制,因此可能導(dǎo)致value很大。

設(shè)想一種場景:

在線音樂app中,某個(gè)歌單有很多用戶收藏,假如有這樣的數(shù)據(jù)結(jié)構(gòu):


image.png

redis的key是歌單ID,redis的value是個(gè)list,list包含了用戶ID , 用戶可能很多,就導(dǎo)致list長度不可控.

大key有什么影響
我們都知道,redis的一個(gè)典型特征就是:核心工作線程是單線程。
單線程中請求任務(wù)的處理是串行的,前面完不成,后面處理不了,同時(shí)也導(dǎo)致分布式架構(gòu)中內(nèi)存數(shù)據(jù)和CPU的不平衡。

執(zhí)行大key命令的客戶端本身,耗時(shí)明顯增加,甚至超時(shí)
執(zhí)行大key相關(guān)讀取或者刪除操作時(shí),會(huì)嚴(yán)重占用帶寬和CPU,影響其他客戶端
大key本身的存儲(chǔ)帶來分布式系統(tǒng)中分片數(shù)據(jù)不平衡,CPU使用率也不平衡
大key有時(shí)候也是熱key,讀取操作頻繁,影響面會(huì)很大
執(zhí)行大key刪除時(shí),在低版本redis中可能阻塞線程
這樣看來大key的影響還是很明顯的,最典型的就是阻塞線程,并發(fā)量下降,導(dǎo)致客戶端超時(shí),服務(wù)端業(yè)務(wù)成功率下降。

大key是如何產(chǎn)生的
大key的產(chǎn)生往往是業(yè)務(wù)方設(shè)計(jì)不合理,沒有預(yù)見vaule的動(dòng)態(tài)增長問題:

一直往value塞數(shù)據(jù),沒有刪除機(jī)制,遲早要爆炸
數(shù)據(jù)沒有合理做分片,將大key變成小key
如何找到大key
增加內(nèi)存&流量&超時(shí)等指標(biāo)監(jiān)控
由于大key的value很大,執(zhí)行讀取時(shí)可能阻塞線程,這樣Redis整體的qps會(huì)下降,并且客戶端超時(shí)會(huì)增加,網(wǎng)絡(luò)帶寬會(huì)上漲,配置這些報(bào)警可以讓我們發(fā)現(xiàn)大key的存在。

bigkeys命令
使用bigkeys命令以遍歷的方式分析Redis實(shí)例中的所有Key,并返回整體統(tǒng)計(jì)信息與每個(gè)數(shù)據(jù)類型中Top1的大Key


image.png

redis-rdb-tools
使用redis-rdb-tools離線分析工具來掃描RDB持久化文件,雖然實(shí)時(shí)性略差,但是完全離線對性能無影響。

redis-rdb-tools是由Python寫的用來分析Redis的rdb快照文件用的工具,它可以把rdb快照文件生成json文件或者生成報(bào)表用來分析Redis的使用詳情。

集成化可視化工具
基于某些公有云或者公司內(nèi)部架構(gòu)的redis一般都會(huì)有可視化的頁面和分析工具,來幫助我們定位大key,當(dāng)然頁面底層也可能是基于bigkeys或者rdb文件離線分析的結(jié)果。

image.png

如何解決大key問題
根據(jù)大key的實(shí)際用途可以分為兩種情況:可刪除和不可刪除。

可刪除


image.png

如果發(fā)現(xiàn)某些大key并非熱key就可以在DB中查詢使用,則可以在Redis中刪掉:

當(dāng)Redis版本大于4.0時(shí),可使用UNLINK命令安全地刪除大Key,該命令能夠以非阻塞的方式,逐步地清理傳入的Key。
Redis UNLINK 命令類似與 DEL 命令,表示刪除指定的 key,如果指定 key 不存在,命令則忽略。
UNLINK 命令不同與 DEL 命令在于它是異步執(zhí)行的,因此它不會(huì)阻塞。
UNLINK 命令是非阻塞刪除,非阻塞刪除簡言之,就是將刪除操作放到另外一個(gè)線程去處理。

當(dāng)Redis版本小于4.0時(shí),避免使用阻塞式命令KEYS,而是建議通過SCAN命令執(zhí)行增量迭代掃描key,然后判斷進(jìn)行刪除。
Redis Scan 命令用于迭代數(shù)據(jù)庫中的數(shù)據(jù)庫鍵。
SCAN 命令是一個(gè)基于游標(biāo)的迭代器,每次被調(diào)用之后, 都會(huì)向用戶返回一個(gè)新的游標(biāo), 用戶在下次迭代時(shí)需要使用這個(gè)新游標(biāo)作為 SCAN 命令的游標(biāo)參數(shù), 以此來延續(xù)之前的迭代過程。

壓縮和拆分key
當(dāng)vaule是string時(shí),比較難拆分,則使用序列化、壓縮算法將key的大小控制在合理范圍內(nèi),但是序列化和反序列化都會(huì)帶來更多時(shí)間上的消耗。

當(dāng)value是string,壓縮之后仍然是大key,則需要進(jìn)行拆分,一個(gè)大key分為不同的部分,記錄每個(gè)部分的key,使用multiget等操作實(shí)現(xiàn)事務(wù)讀取。

當(dāng)value是list/set等集合類型時(shí),根據(jù)預(yù)估的數(shù)據(jù)規(guī)模來進(jìn)行分片,不同的元素計(jì)算后分到不同的片。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容