如何應(yīng)對 Redis 大 Key 問題
日常業(yè)務(wù)運行過程中,Redis 實例經(jīng)常因各種 Big keys / Hot Keys 的問題未及時處理,導(dǎo)致服務(wù)性能下降、訪問超時、用戶體驗變差,甚至可能造成實例大范圍故障 。
這篇文章,我們聊聊生產(chǎn)環(huán)境,如何應(yīng)對 Redis 大 Key 問題。
圖片
一、什么是大 key
大 Key 具體表現(xiàn)為 Redis 中的 Key 對應(yīng)的 Value 很大,占用 Redis 空間比較大,本質(zhì)上是大 Value 問題。
對于 Redis 中不同的數(shù)據(jù)結(jié)構(gòu)類型,常見示例如下所示:
對于 String 類型的 Value 值,值超過 10MB(數(shù)據(jù)值太大)。
對于 Set 類型的 Value 值,含有的成員數(shù)量為 10000 個(成員數(shù)量多)。
對于 List 類型的 Value 值,含有的成員數(shù)量為 10000 個(成員數(shù)量多)。
對于 Hash 格式的 Value 值,含有的成員數(shù)量 1000 個,但所有成員變量的總 Value 值大小為 1000 MB(成員總的體積過大)。
在 Redis 的實際應(yīng)用中,大 Key 問題的定義和評判標準并非固定不變,而是需要結(jié)合具體業(yè)務(wù)場景和性能需求進行綜合考量。
例如,在高并發(fā)、低延遲的敏感場景下,即使 10 KB 的數(shù)據(jù)也可能被視為大 Key;而在低并發(fā)、高吞吐量的離線處理環(huán)境中,大 Key 的閾值可能放寬至 100 KB 甚至更高。
因此,在 Redis 的設(shè)計和使用過程中,應(yīng)該基于業(yè)務(wù)特性和性能指標來制定合理的大 Key 評估標準。
二、大 key 有什么影響
Redis 是單線程執(zhí)行命令 ,當前面的任務(wù)完成不了,那后面的命令就會阻塞,從而導(dǎo)致如下的結(jié)果:
1.請求響應(yīng)時間上升,超時阻塞。
Redis 是單線程架構(gòu),操作大 Key 耗時較長,可能造成請求阻塞。
2.同步中斷或主從切換
內(nèi)存不足時,對大 Key 進行驅(qū)逐操作或者 rename 一個大 Key,容易長時間阻塞主庫,進而可能引發(fā)同步中斷或主從切換。
3.網(wǎng)絡(luò)擁塞
一個大 Key 占用空間是 1MB,每秒訪問1000 次,就有1000 MB 的流量,可能造成實例或局域網(wǎng)的帶寬被占滿,自身服務(wù)變慢,同時影響其他服務(wù)。
4.內(nèi)存使用不均勻
在 Redis 集群架構(gòu)中,某個數(shù)據(jù)分片的內(nèi)存使用率遠超其他數(shù)據(jù)分片,內(nèi)存資源無法達到均衡。另外,Redis 內(nèi)存可能達到 maxmemory 參數(shù)定義的上限,導(dǎo)致重要的 Key 被逐出,甚至引發(fā)內(nèi)存溢出。
需要強調(diào)的是:
對于 Java 應(yīng)用來講,高并發(fā)場景 大 Key 問題容易導(dǎo)致應(yīng)用服務(wù)器 CPU Load / 內(nèi)存占用飆高。
圖片
如圖,這個一個非常標準的通過 redisTemplate 查詢用戶緩存信息的方法。
但當用戶 DTO 對象占用內(nèi)存大小達到 300k ~ 500k 時,并發(fā)高情況下,海量 UserDTO 對象會在新生代產(chǎn)生,對象序列化 和 GC 線程會大量占用 CPU 資源,導(dǎo)致 CPU Load 飆高 ,最終應(yīng)用線程大面積阻塞。
三、大 key 是如何產(chǎn)生的
1.錯誤的技術(shù)選型
比如使用 String 類型的 Key 存放大體積二進制文件型數(shù)據(jù),從而造成 key 對應(yīng)的 value 值特別大 ;
2.List 、Set 數(shù)據(jù)類型數(shù)據(jù)未清理
如圖,我們經(jīng)常使用 Redis List 作為消息隊列,在實際使用中經(jīng)常出現(xiàn)如下問題:生產(chǎn)者發(fā)送消息過快,但消費者消費消息速度低,導(dǎo)致數(shù)據(jù)堆積占用大量內(nèi)存空間 。
3.數(shù)據(jù)沒有合理做分片
業(yè)務(wù)上線前,對業(yè)務(wù)分析不準確,沒有對 Key 中的成員進行合理的拆分,造成個別 Key 中的成員數(shù)量過多。
四、如何找到大 key
1.bigkeys 命令
執(zhí)行 redis-cli 命令時帶上–bigkeys 選項,對整個數(shù)據(jù)庫中的鍵值對大小情況進行統(tǒng)計分析,統(tǒng)計每種數(shù)據(jù)類型的鍵值對個數(shù)以及平均大小。
此外,這個命令執(zhí)行后,會輸出每種數(shù)據(jù)類型中最大的 bigkey 的信息:
- 對于 String 類型來說,會輸出最大 bigkey 的字節(jié)長度
- 對于集合類型來說,會輸出最大 bigkey 的元素個數(shù)
圖片
bigkeys 是通過掃描數(shù)據(jù)庫來查找的,在執(zhí)行的過程中,會對 Redis 實例的性能產(chǎn)生影響。
- 主從集群,建議在從節(jié)點上執(zhí)行該命令,避免阻塞主節(jié)點。
- 沒有從節(jié)點情況下,在 Redis 實例業(yè)務(wù)壓力的低峰階段進行掃描查詢,以免影響到實例的正常運行。
2.監(jiān)控平臺
公有云或者公司內(nèi)部架構(gòu)部門一般都有監(jiān)控平臺,可以可視化分析 Redis 服務(wù)監(jiān)控指標。
如下圖是阿里云的 Redis 監(jiān)控大 Key 分析界面 。
圖片
假如是架構(gòu)部門自己的監(jiān)控平臺,可以添加 Redis 的 Key 監(jiān)控統(tǒng)計。
下圖是UMP 監(jiān)控平臺的設(shè)計思路:
圖片
流程如下:
- 業(yè)務(wù)系統(tǒng)引入通 UMP SDK ,當業(yè)務(wù)系統(tǒng)運行時,SDK 會將日志文件(JVM、TP 、HeatBeat)寫到磁盤 ;
- FileBeat 讀取日志文件,發(fā)送到 Kafka ;
- UMP 計算服務(wù) 從 Kafka 中獲取消息,根據(jù)消息類型,執(zhí)行分析邏輯(JVM、TP 、HeatBeat );
- 計算完成之后,指標結(jié)果數(shù)據(jù)存儲到 Hbase,MySQL 用于存儲元數(shù)據(jù),Redis 用于存儲臨時計算數(shù)據(jù) ;
- 研發(fā)人員登錄控制臺查看監(jiān)控信息 ,核心的監(jiān)控數(shù)據(jù)存儲在 Hbase 中,通過 HighChart 組件渲染。
UMP 可以對應(yīng)用端的 Redis 操作實現(xiàn)全面的監(jiān)控,包括命令超時、Key大小、使用頻率等關(guān)鍵指標。
五、如何解決大 key 問題
1.清理無效的數(shù)據(jù)
主要針對 list 和 set 這種類型,在使用的過程中,list 和 set 中對應(yīng)的內(nèi)容不斷增加,需要定時的對 list 和 set 進行清理。
2.壓縮對應(yīng)的大 Key 的 Value
通過序列化或者壓縮的方法對 value 進行壓縮,使其變?yōu)檩^小的 value,但是如果壓縮之后如果對應(yīng)的 value 還是特別大的話,就需要使用拆分的方法進行解決。
3.針對大 Key 進行拆分
通過將 BigKey 拆分成多個小 Key 的鍵值對,并且拆分后的對應(yīng)的 value 大小和拆分成的成員數(shù)量比較合理,然后進行存儲即可,在獲取的時候通過 get 不同的 key 或是用 mget 批量獲取存儲的鍵值對。
4.實時監(jiān)控 Redis 內(nèi)存、帶寬及 Key 增長變化趨勢
通過監(jiān)控系統(tǒng),監(jiān)控 Redis 中的內(nèi)存占用大小和網(wǎng)絡(luò)帶寬的占用大小,以及固定時間內(nèi)的內(nèi)存占用增長率,當超過設(shè)定的閾值的時候,進行報警通知處理。