一文了解如何發(fā)現(xiàn)并解決Redis熱key與大key問題
業(yè)務(wù)場景中經(jīng)常會有各種熱key或大key的問題,如果未能及時處理,可能會導致服務(wù)性能下降、用戶體驗變差,甚至引發(fā)大面積故障。所以本文針對這兩個問題進行講解,提供發(fā)現(xiàn)/監(jiān)控的方法以及處理的解決方案。
熱Key問題
什么是熱key?
熱key是服務(wù)端的常見問題,指一段時間內(nèi)某個key的訪問量遠遠超過其他的key,導致大量訪問流量落在某一個redis實例中;或者是帶寬使用率集中在特定的key(例如,對一個包含2000個field的hash key每秒發(fā)送大量的hgetall操作請求);又或者是cpu使用時間占比集中在特定的key(例如,對一個包含10000個field的key每秒發(fā)送大量的zrange操作請求)。
以被請求頻率來定義是否是熱key,沒有固定經(jīng)驗值。某個key被高頻訪問導致系統(tǒng)穩(wěn)定性變差,都可以定義為熱key。
可能造成的問題
熱點緩存會導致流量集中,redis緩存與數(shù)據(jù)庫被擊穿,從而引發(fā)系統(tǒng)雪崩。詳情可以看《快速了解緩存穿透與緩存雪崩》。
請求分配不均,存在熱key的節(jié)點面臨較大的訪問壓力,可能出現(xiàn)該數(shù)據(jù)分片的連接數(shù)被耗盡甚至宕機。(即使采取擴容也會對資源有很大的浪費)
發(fā)現(xiàn)方法
由于熱key發(fā)生對系統(tǒng)穩(wěn)定性有巨大危害,所以需要上線前設(shè)立故障預案、建立監(jiān)控和報警機制,以便快速響應(yīng)故障。
根據(jù)業(yè)務(wù)經(jīng)驗,預估哪些是熱key。
優(yōu)點:簡單直接。
缺點:但并不是所有業(yè)務(wù)都能預估出哪些key是熱key。
在客戶端收集。在操作redis之前,加上統(tǒng)計頻次的邏輯,然后將統(tǒng)計數(shù)據(jù)發(fā)送給一個聚合計算的服務(wù)進行統(tǒng)計。
優(yōu)點:方案簡單。
缺點:無法支持大公司多語言環(huán)境的SDK,或者說多語言SDK對齊比較困難。此外SDK的維護升級成本會很高。
在proxy層收集。有些服務(wù)在請求redis之前會請求一個proxy服務(wù),這種場景可以使用在proxy層收集熱key數(shù)據(jù),收集機制類似于在客戶端收集。
優(yōu)點:方案對使用方完全透明;沒有SDK多語言異構(gòu)和升級成本高的問題。(不理解這個地方的話,可以查看小輝之前的博客《通用能力抽象選擇SDK組件還是API服務(wù)?》)
缺點:并不是所有場景都會有proxy層。
redis集群監(jiān)控。如果出現(xiàn)某個實例qps傾斜,說明可能存在熱key。
優(yōu)點:不需要額外開發(fā)。
缺點:每次發(fā)生狀況需要人工排查,因為熱key只是導致qps傾斜的一種可能。
redis 4.0版本之后熱點key發(fā)現(xiàn)功能。執(zhí)行redis-cli時加上–-hotkeys選項即可。
優(yōu)點:不需要額外開發(fā)。
缺點:該參數(shù)在執(zhí)行的時候,如果key比較多,執(zhí)行耗時會非常長,由此導致查詢結(jié)果的實時性并不好。
redis客戶端使用TCP協(xié)議與服務(wù)端進行交互。通過腳本監(jiān)聽端口,解析網(wǎng)絡(luò)包并進行分析。
優(yōu)點:對原有的業(yè)務(wù)系統(tǒng)沒有改造。
缺點:開發(fā)成本高,維護困難,有丟包可能性。
常用的處理方法
熱key統(tǒng)計可以使用LFU數(shù)據(jù)結(jié)構(gòu)并結(jié)合上面的發(fā)現(xiàn)方法,將最熱t(yī)opN的key進行統(tǒng)計,然后在client端使用本地緩存,從而降低redis集群對熱key的訪問量,但這種方法帶來兩個問題:
如果對所有熱key進行本地緩存,那么本地緩存是否會過大,從而影響應(yīng)用程序本身的性能開銷。
可能需要保證本地緩存和redis數(shù)據(jù)的一致性。
將熱key加上前綴或者后綴,把熱key的數(shù)量從1個變成實例個數(shù),利用分片特性將這n個key分散在不同節(jié)點上,這樣就可以在訪問的時候,采用客戶端負載均衡的方式,隨機選擇一個key進行訪問,將訪問壓力分散到不同的實例中。這個方案有個明顯的缺點,就是緩存的維護成本大:假如有n為100,則更新或者刪除key的時候需要操作100個key。
利用讀寫分離,通過主從復制的方式,增加slave節(jié)點來實現(xiàn)讀請求的負載均衡。這個方案明顯的缺點就是使用機器硬抗熱key的數(shù)據(jù),資源耗費嚴重;而且引入讀寫分離架構(gòu),增加節(jié)點數(shù)量,都會增加系統(tǒng)的復雜度降低穩(wěn)定性。
大Key問題
什么是大key?
大key是指當redis的字符串類型占用內(nèi)存過大或非字符串類型元素數(shù)量過多。
生產(chǎn)環(huán)境中,綜合衡量運維和環(huán)境的情況,給大key定義參考值如下:
string類型的key超過10KB
hash/set/zset/list等數(shù)據(jù)結(jié)構(gòu)中元素個數(shù)大于5k/整體占用內(nèi)存大于10MB
不同系統(tǒng)性能條件不同,所以建議這個標準設(shè)置保守些,以系統(tǒng)穩(wěn)定性為第一考量
可能造成的問題
內(nèi)存使用不均勻。例如在redis集群模式中,某個數(shù)據(jù)分片的內(nèi)存使用率遠超其他數(shù)據(jù)分片,無法使數(shù)據(jù)分片的內(nèi)存資源達到均衡。另外也可能造成redis內(nèi)存達到
maxmemory參數(shù)定義的上限導致重要的Key被逐出,甚至引發(fā)內(nèi)存溢出。響應(yīng)時間上升、超時阻塞。由于redis是單線程架構(gòu),操作大key耗時較長,有可能造成redis阻塞。
過期時可能阻塞。大key設(shè)定了過期時間,當過期時這個key會被刪除。假如redis版本低于4.0沒有非同步刪除機制,就會存在阻塞redis的可能性,并且慢查詢查不到;同樣,內(nèi)存不足時的key驅(qū)逐或者是rename一個大key也會阻塞redis服務(wù)。長時間阻塞主庫,可能會引發(fā)同步中斷或主從切換。
慢查詢?yōu)槭裁床椴坏?。舉例,如果請求進來且redis服務(wù)器正在進行過期鍵掃描,需要等待100毫秒。當客戶端設(shè)置的超時時間小于100毫秒,那就會導致連接因為超時而關(guān)閉,就會造成異常,這些現(xiàn)象并不能從慢查詢?nèi)罩局胁樵兊剑ㄒ驗槁樵冎挥涗涍壿嬏幚磉^程,不包括等待時間)。
網(wǎng)絡(luò)擁塞。例如:一個大key占用空間是1MB,每秒訪問1000次,就有1000MB的流量,可能造成機器或局域網(wǎng)的帶寬被打滿,同時波及其他服務(wù)。
發(fā)現(xiàn)方法
使用工具定期掃描,并建立好監(jiān)控和通知機制。
redis-cli --bigkeys命令??梢杂脕碚业侥硞€實例5種數(shù)據(jù)類型(string、hash、list、set、zset)最大的key。
優(yōu)點:不阻塞服務(wù)
缺點:信息較少(只有各類型最大的key信息),內(nèi)容不夠精確(例如hash/list/set/zset都是以元素個數(shù)衡量大key,但實際上元素個數(shù)多不代表占用內(nèi)存大)。
redis-rdb-tools工具。redis實例上執(zhí)行bgsave,然后對dump出來的rdb文件進行分析。
優(yōu)點:獲取信息更詳細
缺點:需要離線操作,獲取結(jié)果時間較長
Redis4.0之后,新增memory usage命令,通過隨機抽樣field的方式估算key的大?。颖驹酱螅h(huán)次數(shù)越多,計算結(jié)果越精確,性能消耗也越多)。編寫python腳本,利用scan和memory usage命令,可以在集群低峰的時候掃描redis,排查大key。
優(yōu)點:獲取信息較準確且及時
缺點:python腳本需要注意不能影響線上正常服務(wù),設(shè)置好監(jiān)控和熔斷。
常用的處理方法
大key非熱key,如果不是必要的信息,可以直接刪除del或者unlink都可以。
如果是redis4.0之前的版本,建議對于key使用(scan/sscan/hscan/zscan),將大key逐步刪除(ltrim/zremrangebyscore/hdel/srem)。redis4.0之后,直接使用unlink替換del,會有后臺線程將大key異步刪除。
業(yè)務(wù)拆分,將key的含義更細粒度化,避免大key出現(xiàn)。
數(shù)據(jù)結(jié)構(gòu)上拆分。如果大key是個大json,可以通過mset的方式,將這個key的內(nèi)容打散到各個實例中,減小大key對數(shù)據(jù)量傾斜的影響;如果是大list,可以拆成
list_1,list_2,list_N;其他數(shù)據(jù)結(jié)構(gòu)同理。(可以考慮增加單獨key存儲大key被拆分的個數(shù)或元數(shù)據(jù)信息)在redis沒有開啟非同步刪除機制的場景下,設(shè)置過期時間時,一定要避免大批量鍵同時過期的現(xiàn)象,所以如果有這種情況,最好給過期時間加個隨機范圍,緩解大量鍵同時過期,造成客戶端等待超時的現(xiàn)象。
對于長文本,更建議使用文檔型數(shù)據(jù)庫例如MongoDB等。
對一致性要求不高的場景,嘗試使用客戶端緩存。(只解決了redis的阻塞問題,但機器或局域網(wǎng)的帶寬問題沒有改善)
對大key的壓縮。相當于用cpu資源來降低網(wǎng)絡(luò)io,其中g(shù)oogle提出的snappy算法較常用。
對于hash等數(shù)據(jù)結(jié)構(gòu),需要注意業(yè)務(wù)是否可以引入定期清理無效field的機制。
