詳解Redis中的BigKey如何發(fā)現(xiàn)和處理
什么是BigKey?
通常來說,如果一個鍵值對大于一定閾值(例如 1MB),它就可以被認(rèn)為是一個大鍵或 bigkey。
大鍵的存在通常被認(rèn)為是不好的,主要原因:
1.占用大量內(nèi)存,可能導(dǎo)致內(nèi)存不足
2.增加內(nèi)存碎片,降低內(nèi)存利用效率
3.增加SAVE和復(fù)制時間
4.增加AOF重寫時間
5.壓縮列表元素過多,降低查找效率
所以在使用 Redis 時,應(yīng)該盡量避免 bigkey 的產(chǎn)生。
常見的bigkey原因:
1.一個 Hash 類型鍵值對字段過多
2.一個 Set 類型包含了過多的成員
3.一個 List 類型包含了過多的元素
4.一個 Bitmaps 類型映射了過大的內(nèi)容
5.一個 Zset 類型包含了過多的成員
一般建議 bigkey 的大小控制在 1MB 以內(nèi)為宜。
BigKey危害?
Redis 中存在的BigKey會帶來以下幾個方面的危害:
占用大量內(nèi)存空間
BigKey由于值過于龐大,會占用Redis sehr大比例的內(nèi)存,從而對Redis的內(nèi)存使用造成浪費和壓力。
阻塞服務(wù)器進(jìn)程
當(dāng)需要對BigKey進(jìn)行操作如刪除、更改值時,這些操作需要花費較長時間,會造成Redis主進(jìn)程阻塞,影響Redis的正常工作。
加長持久化時間
Redis需要進(jìn)行持久化將數(shù)據(jù)寫入磁盤保存,BigKey會因其數(shù)據(jù)量大而導(dǎo)致持久化過程非常緩慢。
延長復(fù)制時間
當(dāng)Redis進(jìn)行主從復(fù)制時,同步BigKey也會非常慢,可能導(dǎo)致從服務(wù)器的數(shù)據(jù)同步效率下降。
增加內(nèi)存碎片
BigKey的大值會導(dǎo)致Redis內(nèi)存中產(chǎn)生大量不連續(xù)的碎片,降低內(nèi)存利用效率。
加重AOF重寫壓力
BigKey會導(dǎo)致AOF文件過大,當(dāng)Redis進(jìn)行AOF重寫時,需要處理大量數(shù)據(jù),加重服務(wù)器壓力。
降低查找效率
對于一些集合性的數(shù)據(jù)結(jié)構(gòu)如Hash、List等,BigKey中的數(shù)據(jù)量龐大,會降低查找效率。
所以,對于Redis來說,發(fā)現(xiàn)并解決BigKey問題是非常重要的。
如何發(fā)現(xiàn)BigKey?
在Redis中,可以通過以下幾種方式發(fā)現(xiàn)BigKey:
info命令
使用Redis的info命令,查看keyspace部分,觀察biggest_key_size的大小,當(dāng)biggest_key_size過大時,說明存在BigKey。
可以通過analyze命令的keyspace部分來觀察biggest_key_size,判斷是否存在BigKey。例如:
127.0.0.1:6379> info keyspace # Keyspace db0:keys=5,expires=0,avg_ttl=0 db1:keys=8,expires=0,avg_ttl=0 db2:keys=4,expires=0,avg_ttl=0 db3:keys=5,expires=0,avg_ttl=0 db4:keys=4,expires=0,avg_ttl=0 db5:keys=5,expires=0,avg_ttl=0 db6:keys=5,expires=0,avg_ttl=0 db7:keys=4,expires=0,avg_ttl=0 db8:keys=4,expires=0,avg_ttl=0 db9:keys=4,expires=0,avg_ttl=0 db10:keys=5,expires=0,avg_ttl=0 db11:keys=5,expires=0,avg_ttl=0 db12:keys=5,expires=0,avg_ttl=0 db13:keys=4,expires=0,avg_ttl=0 db14:keys=4,expires=0,avg_ttl=0 db15:keys=4,expires=0,avg_ttl=0 # ... 略 biggest_key_size:8192
從上面的biggest_key_size大小可以看出,目前存在value超過8KB的大鍵。
scan命令
使用scan命令迭代Redis中的鍵,同時利用Redis的debug object命令查看key對應(yīng)的value大小,如果超過閾值即判定為BigKey。
可以通過SCAN命令迭代Redis中的鍵,并配合DEBUG OBJECT命令判斷鍵值大小,來發(fā)現(xiàn)BigKey。例如:
127.0.0.1:6379> scan 0 match * count 10 1) "17" 2) 1) "key:1" 2) "key:2" 3) "key:3" 4) "key:4" 5) "bigkey" 127.0.0.1:6379> debug object bigkey Value at:0x7fdafb0258e0 refcount:1 encoding:raw serializedlength:1001927
上面先通過SCAN命令掃描出鍵,然后使用DEBUG OBJECT判斷bigkey的值大小,結(jié)果顯示serializedlength超過1MB。所以這是一個BigKey。我們可以編寫一個循環(huán)使用SCAN的程序,設(shè)置一個大小閾值,所有值超過閾值的鍵都記錄下來,從而掃描出所有BigKey。相比直接遍歷鍵空間,SCAN實現(xiàn)漸進(jìn)式掃描,可以避免命令阻塞,更安全的發(fā)現(xiàn)BigKey。
Redis-cli
使用redis-cli工具的–bigkeys參數(shù),該參數(shù)會掃描Redis中的鍵并返回所有大小超過指定閾值的BigKey。
第三方工具
使用一些第三方Redis可視化管理工具,如Redis Enterprise、Astra等,這些工具提供了BigKey檢測功能。
日志監(jiān)控
分析Redis日志,關(guān)注slave緩慢或者持久化被block的情況,這可能與BigKey有關(guān)。
定期主動掃描
可以通過腳本等定期主動掃描Redis鍵值,當(dāng)鍵值超過閾值則記錄為BigKey。
下面寫一個腳本示例
import redis import time # Redis連接 redis_client = redis.Redis(host='localhost', port=6379) # 大鍵閾值 - 100MB BIG_KEY_THRESHOLD = 100 * 1024 * 1024 # 掃描間隔 - 1小時 SCAN_INTERVAL = 3600 def scan_big_keys(): cursor = '0' big_keys = [] while cursor != 0: cursor, keys = redis_client.scan(cursor=cursor, count=100) for key in keys: size = redis_client.debug_object(key)['serializedlength'] if size > BIG_KEY_THRESHOLD: big_keys.append({'key': key, 'size': size}) return big_keys while True: big_keys = scan_big_keys() if big_keys: print(f'Found {len(big_keys)} big keys') for bk in big_keys: print(f' {bk["key"]} {bk["size"]}') time.sleep(SCAN_INTERVAL)
主要步驟包括:
? 1.使用SCAN命令漸進(jìn)式掃描鍵空間
? 2.調(diào)用DEBUG OBJECT命令獲取鍵值大小
? 3.比較大小判斷是否為大鍵
? 4.定期循環(huán)掃描這個腳本可以靈活調(diào)整大鍵閾值、掃描間隔等參數(shù)。
你可以將它部署為持續(xù)運行的任務(wù),自動發(fā)現(xiàn)Redis中的大鍵。
綜合利用上述各種方式,可以有效發(fā)現(xiàn)Redis中存在的BigKey,作為后續(xù)優(yōu)化的依據(jù)。
如何刪除BigKey?
在Redis中刪除BigKey可以通過以下幾種方法:
DEL命令
直接使用DEL命令刪除鍵即可快速刪除單個BigKey,但是這會導(dǎo)致數(shù)據(jù)丟失。
DEL big_key
重新設(shè)計鍵
如果BigKey是由于鍵設(shè)計不當(dāng)導(dǎo)致的,那么可以重新設(shè)計鍵結(jié)構(gòu),拆分BigKey。
例如將原來的一個Hash BigKey拆分為多個Hash小Key。
使用UNLINK
UNLINK可以異步刪除鍵,不會堵塞服務(wù)器。但也存在數(shù)據(jù)丟失風(fēng)險。
UNLINK big_key
配合事務(wù)操作
可以先用MULTI開啟事務(wù),再使用GET、DEL等命令處理BigKey,最后用EXEC提交事務(wù)。這樣可以避免阻塞服務(wù)器。
分段刪除
對于List、Hash等結(jié)構(gòu)的BigKey,可以通過區(qū)間查找、分段刪除的方式進(jìn)行分批刪除,避免一次性刪除耗時太長。
使用SCAN命令
可以通過SCAN命令漸進(jìn)式地掃描并刪除BigKey。
熱重啟
可以考慮停止Redis服務(wù),并刪除持久化文件(rdb、aof),然后重啟,這樣可以直接清除所有BigKey,但會損失全部數(shù)據(jù)。
可以根據(jù)實際情況選擇最佳方案刪除BigKey。
Hash類型Bigkey如何處理?
對于Redis中的Hash類型Bigkey,可以通過以下幾種方式進(jìn)行優(yōu)化:
拆分Bigkey
最直接的方法是將一個大的Hash Key拆分為多個小的Hash Key,防止單個鍵值對過大。例如
將user:{uid} 拆分為 user:{uid}:info、user:{uid}:data等。
使用Hash數(shù)據(jù)結(jié)構(gòu)
Redis的Hash相比字符串可以大幅度減少內(nèi)存使用??梢钥紤]將字符串值轉(zhuǎn)換為Hash結(jié)構(gòu),字段由字符串改為Hash。
海量計數(shù)器轉(zhuǎn)換為Bitmaps
如果Hash中的字段是簡單的計數(shù)器,可以考慮用Bitmaps替代,由于Bitmaps有壓縮特性,可以減少內(nèi)存使用。
采用更緊湊的編碼
對于Hash的值,如果是數(shù)字,可以選擇更緊湊的編碼方式,如整形float編碼轉(zhuǎn)為整數(shù)int編碼。
控制字段的數(shù)量
對Hash字段數(shù)量進(jìn)行限制,避免單個Hash過多字段,導(dǎo)致結(jié)構(gòu)過大。
使用內(nèi)存優(yōu)化參數(shù)
調(diào)整Redis的ziplist、hash-max-ziplist-value等參數(shù),優(yōu)化內(nèi)存使用。
redis>config get hash-max-ziplist-entries 1) "hash-max-ziplist-entries" 2) "512" --默認(rèn)原來是512 redis>config set hash-max-ziplist-entries 1000 "OK" redis>config get hash-max-ziplist-entries 1) "hash-max-ziplist-entries" 2) "1000" --調(diào)整為1000,但是不建議調(diào)整超過1000
海量小值分離
如果Hash中存在大量小值字段,可以將這些小值字段拆分出去,單獨用一個Hash Key存儲。
假設(shè)我們有一個 user:{uid} 的 hash 類型 bigkey,里面存儲了用戶的名稱、年齡、手機(jī)號等信息。其中手機(jī)號字段的值都是數(shù)字且較小。
我們可以這樣進(jìn)行海量小值的分離:
為手機(jī)號創(chuàng)建新的 hash key
user:{uid}:phone
遍歷 user:{uid} 的 hash,提取所有手機(jī)號字段和值,存入 user:{uid}:phone
HGETALL user:{uid} # 遍歷獲取所有字段 ... HSET user:{uid}:phone {phone_field} {phone_value} # 存儲到新key
從 user:{uid} 刪除這些手機(jī)號字段
HDEL user:{uid} {phone_field_1} {phone_field_2} ... # 刪除手機(jī)號字段
這樣我們就將海量的手機(jī)號小值從大key中分離出來,單獨用一個 hash 存儲。
分離后的優(yōu)點:
1.原來的 user:{uid} 鍵值對會縮小,不再存在大量小值
2.由于是小值,在新 key 中可以使用更緊湊的內(nèi)存編碼存儲
3.查找手機(jī)號等小值時,只需要操作 user:{uid}:phone 即可,避免遍歷全量大key
綜合使用這些優(yōu)化策略,可以有效減小Hash類型Bigkey的內(nèi)存占用。
寫在最后
以上就是詳解Redis中的BigKey如何發(fā)現(xiàn)和處理的詳細(xì)內(nèi)容,更多關(guān)于Redis BigKey發(fā)現(xiàn)和處理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
超強、超詳細(xì)Redis數(shù)據(jù)庫入門教程
這篇文章主要介紹了超強、超詳細(xì)Redis入門教程,本文詳細(xì)介紹了Redis數(shù)據(jù)庫各個方面的知識,需要的朋友可以參考下2014-10-10Redis從單點到集群部署模式(單機(jī)模式?主從模式?哨兵模式)
這篇文章主要為大家介紹了Redis從單點集群部署模式(單機(jī)模式?主從模式?哨兵模式)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11