簡介: 在Redis的使用過程中,我們經常會遇到BigKey(下文將其稱為“大key”)及HotKey(下文將其稱為“熱key”)。大Key與熱Key如果未能及時發現並進行處理,很可能會使服務性能下降、用戶體驗變差,甚至引發大面積故障。
作者 | 煙圈
來源 | 阿里技術公眾號
一 前言
在Redis的使用過程中,我們經常會遇到BigKey(下文將其稱為“大key”)及HotKey(下文將其稱為“熱key”)。大Key與熱Key如果未能及時發現並進行處理,很可能會使服務性能下降、用戶體驗變差,甚至引發大面積故障。
二 大Key與熱Key的定義
我們經常能夠在公司內部的Redis開發使用規范手冊,或網絡中大量的Redis最佳實踐文章里看到有關大Key、熱Key的定義,然而這些資料中的大Key熱Key判定標准卻不盡相同,但可以明確的是,它們的判定維度是一致的:大Key通常都會以數據大小與成員數量來判定,而熱Key則以其接收到的請求頻率、數量來判定。
1 什么是大Key
通常我們會將含有較大數據或含有大量成員、列表數的Key稱之為大Key,下面我們將用幾個實際的例子對大Key的特征進行描述:
- 一個STRING類型的Key,它的值為5MB(數據過大)
- 一個LIST類型的Key,它的列表數量為20000個(列表數量過多)
- 一個ZSET類型的Key,它的成員數量為10000個(成員數量過多)
- 一個HASH格式的Key,它的成員數量雖然只有1000個但這些成員的value總大小為100MB(成員體積過大)
需要注意的是,在以上的例子中,為了方便理解,我們對大Key的數據、成員、列表數給出了具體的數字。為了避免誤導,在實際業務中,大Key的判定仍然需要根據Redis的實際使用場景、業務場景來進行綜合判斷。
2 什么是熱Key
在某個Key接收到的訪問次數、顯著高於其它Key時,我們可以將其稱之為熱Key,常見的熱Key如:
- 某Redis實例的每秒總訪問量為10000,而其中一個Key的每秒訪問量達到了7000(訪問次數顯著高於其它Key)
- 對一個擁有上千個成員且總大小為1MB的HASH Key每秒發送大量的HGETALL(帶寬占用顯著高於其它Key)
- 對一個擁有數萬個成員的ZSET Key每秒發送大量的ZRANGE(CPU時間占用顯著高於其它Key)
三 大Key與熱Key帶來的問題
在Redis的使用中,大Key及熱Key會給Redis帶來各種各樣的問題,而最常見的問題為性能下降、訪問超時、數據不均衡等。
1 大Key帶來的常見問題
- Client發現Redis變慢;
- Redis內存不斷變大引發OOM,或達到maxmemory設置值引發寫阻塞或重要Key被逐出;
- Redis Cluster中的某個node內存遠超其余node,但因Redis Cluster的數據遷移最小粒度為Key而無法將node上的內存均衡化;
- 大Key上的讀請求使Redis占用服務器全部帶寬,自身變慢的同時影響到該服務器上的其它服務;
- 刪除一個大Key造成主庫較長時間的阻塞並引發同步中斷或主從切換;
2 熱Key帶來的常見問題
- 熱Key占用大量的Redis CPU時間使其性能變差並影響其它請求;
- Redis Cluster中各node流量不均衡造成Redis Cluster的分布式優勢無法被Client利用,一個分片負載很高而其它分片十分空閑從而產生讀/寫熱點問題;
- 在搶購、秒殺活動中,由於商品對應庫存Key的請求量過大超出Redis處理能力造成超賣;
- 熱Key的請求壓力數量超出Redis的承受能力造成緩存擊穿,此時大量強求將直接指向后端存儲將其打掛並影響到其它業務;
四 大Key與熱Key的常見產生原因
業務規划不足、Redis不正確的使用、無效數據的堆積、訪問突增等都會產生大Key與熱Key,如:
- 將Redis用在並不適合其能力的場景,造成Key的value過大,如使用String類型的Key存放大體積二進制文件型數據(大Key);
- 業務上線前規划設計考慮不足沒有對Key中的成員進行合理的拆分,造成個別Key中的成員數量過多(大Key);
- 沒有對無效數據進行定期清理,造成如HASH類型Key中的成員持續不斷的增加(大Key);
- 預期外的訪問量陡增,如突然出現的爆款商品、訪問量暴漲的熱點新聞、直播間某大主播搞活動帶來的大量刷屏點贊、游戲中某區域發生多個工會間的戰斗涉及大量玩家等(熱Key);
- 使用LIST類型Key的業務消費側代碼故障,造成對應Key的成員只增不減(大Key);
五 找出Redis中的大Key與熱Key
大Key與熱Key的分析並不困難,我們有多種途徑和手段來對Redis中的Key進行分析並找出其中的“問題”Key,如Redis的內置功能、開源工具、阿里雲Redis控制台中的Key分析功能等。
1 使用Redis內置功能發現大Key及熱Key
Redis內置的一些命令、工具都可以幫助我們來發現這些問題Key。當你對Redis的大Key熱Key已有明確的分析目標時,可以通過如下命令對對應Key進行分析。
通過Redis內置命令對目標Key進行分析
可能你會選擇使用debug object命令對Key進行分析。該命令能夠根據傳入的對象(Key的名稱)來對Key進行分析並返回大量數據,其中serializedlength的值為該Key的序列化長度,你可能會選擇通過該數據來判斷對應Key是否符合你的大Key判定標准。
需要注意的是,Key的序列化長度並不等同於它在內存空間中的真實長度,此外,debug object屬於調試命令,運行代價較大,並且在其運行時,進入Redis的其余請求將會被阻塞直到其執行完畢。而該命令的運行的時間長短取決於傳入對象(Key名)序列化長度的大小,因此,在線上環境中並不推薦使用該命令來分析大Key,這可能引發故障。
Redis自4.0起提供了MEMORY USAGE命令來幫助分析Key的內存占用,相對debug object它的執行代價更低,但由於其時間復雜度為O(N)因此在分析大Key時仍有阻塞風險。
我們建議通過風險更低方式來對Key進行分析,Redis對於不同的數據結構提供了不同的命令來返回其長度或成員數量,如下表:
通過以上Redis內置命令我們可以方便且安全的對Key進行分析而不會影響線上服務,但由於它們返回的結果非Key的真實內存占用數據,因此不夠精確,僅可作為參考。
通過Redis官方客戶端redis-cli的bigkeys參數發現大Key
如果你並無明確的目標Key用於分析,而是希望通過工具找出整個Redis實例中的大Key,此時redis-cli的bigkeys參數能夠方便的幫你實現這個目標。
Redis提供了bigkeys參數能夠使redis-cli以遍歷的方式分析整個Redis實例中的所有Key並匯總以報告的方式返回結果。該方案的優勢在於方便及安全,而缺點也非常明顯:分析結果不可定制化。
bigkeys僅能分別輸出Redis六種數據結構中的最大Key,如果你想只分析STRING類型或是找出全部成員數量超過10的HASH Key,那么bigkeys在此類需求場景下將無能為力。
GitHub上有大量的開源項目能夠實現bigkeys的加強版使結果能夠按照配置定制化輸出,另外你可也以動手使用SCAN + TYPE並配合上文表格中的命令自己實現一個Redis實例級的大Key分析工具。
同樣,該方案的實現方式及返回結果使其不具備精確性與實時性,建議僅作為參考。
通過Redis官方客戶端redis-cli的hotkeys參數發現熱Key
Redis自4.0起提供了hotkeys參數來方便用戶進行實例級的熱Key分析功,該參數能夠返回所有Key的被訪問次數,它的缺點同樣為不可定制化輸出報告,大量的信息會使你在分析結果時復雜度較大,另外,使用該方案的前提條件是將redis-server的maxmemory-policy參數設置為LFU。
通過業務層定位熱Key
指向Redis的每一次訪問都來自業務層,因此我們可以通過在業務層增加相應的代碼對Redis的訪問進行記錄並異步匯總分析。該方案的優勢為能夠准確並及時的分析出熱Key的存在,缺點為業務代碼復雜度的增加,同時可能會降低一些性能。
使用monitor命令在緊急情況時找出熱Key
Redis的monitor命令能夠忠實的打印Redis中的所有請求,包括時間信息、Client信息、命令以及Key信息。在發生緊急情況時,我們可以通過短暫執行monitor命令並將輸出重定向至文件,在關閉monitor命令后通過對文件中請求進行歸類分析即可找出這段時間中的熱Key。
由於monitor命令對Redis的CPU、內存、網絡資源均有一定的占用。因此,對於一個已處於高壓狀態的Redis,monitor可能會起到雪上加霜的作用。同時,這種異步收集並分析的方案的時效性較差,並且由於分析的精確度取決於monitor的執行時間,因此在多數無法長時間執行該命令的線上場景中本方案的精確度也不夠好。
2 使用開源工具發現大Key
Redis的高度流行使我們能夠方便的找到大量開源方案來解決我們當前遇到的難題:在不影響線上服務的同時得到精確的分析報告。
使用redis-rdb-tools工具以定制化方式找出大Key
如果你希望按照自己的標准精確的分析一個Redis實例中所有Key的真實內存占用並避免影響線上服務,在分析結束后得到一份簡潔易懂的報告,redis-rdb-tools是非常好的選擇。
該工具能夠對Redis的RDB文件進行定制化的分析,但由於分析RDB文件為離線工作,因此對線上服務不會有任何影響,這是它的最大優點但同時也是它的最大缺點:離線分析代表着分析結果的較差時效性。對於一個較大的RDB文件,它的分析可能會持續很久很久。
3 依靠公有雲的Redis分析服務發現大Key及熱Key
如果你期望能夠實時的對Redis實例中的所有Key進行分析並發現當前存在的大Key及熱Key、了解Redis在運行時間線中曾出現過哪些大Key熱Key,使自己對整個Redis實例的運行狀態有一個全面而又准確的判斷,那么公有雲的Redis控制台將能滿足這個需求。
阿里雲Redis控制台中的CloudDBA
CloudDBA是阿里雲的數據庫智能服務系統,它支持Redis大Key與熱Key的實時分析、發現。
大Key及熱Key分析底層為阿里雲Redis內核的Key分析功能,該功能通過Redis內核直接發現並輸出大Key熱Key的相關信息,因此,該功能的分析結果准確性高效且對性能幾乎無任何影響,你可以通過點擊CloudDBA中的“Key分析”進入該功能,如下圖1-1:
圖1-1:阿里雲Redis控制台CloudDBA
Key分析功能共有兩個頁面,它們允許在不同的時間維度對對應Redis實例中的Key進行分析:
- 實時:對當前實例立即開始分析當前實例,展示當前存在的所有大Key及熱Key。
- 歷史:展示該實例近期曾出現過的大Key及熱Key,在歷史頁面中,所有出現過的大Key及熱Key都會被記錄,哪怕這些Key當前已經不存在。該功能能夠很好的反映Redis的歷史Key狀態,幫助追溯過去或現場已遭破壞的問題。
六 大Key與熱Key的處理
現在,我們已經通過多種手段找到了Redis中的問題Key,那么我們應當立即着手對他們進行處理,避免它們在之后的時間中引發問題。
1 大Key的常見處理辦法
對大Key進行拆分
如將一個含有數萬成員的HASH Key拆分為多個HASH Key,並確保每個Key的成員數量在合理范圍,在Redis Cluster結構中,大Key的拆分對node間的內存平衡能夠起到顯著作用。
對大Key進行清理
將不適合Redis能力的數據存放至其它存儲,並在Redis中刪除此類數據。需要注意的是,我們已在上文提到一個過大的Key可能引發Redis集群同步的中斷,Redis自4.0起提供了UNLINK命令,該命令能夠以非阻塞的方式緩慢逐步的清理傳入的Key,通過UNLINK,你可以安全的刪除大Key甚至特大Key。
時刻監控Redis的內存水位
突然出現的大Key問題會讓我們措手不及,因此,在大Key產生問題前發現它並進行處理是保持服務穩定的重要手段。我們可以通過監控系統並設置合理的Redis內存報警閾值來提醒我們此時可能有大Key正在產生,如:Redis內存使用率超過70%,Redis內存1小時內增長率超過20%等。
通過此類監控手段我們可以在問題發生前解決問題,如:LIST的消費程序故障造成對應Key的列表數量持續增長,將告警轉變為預警從而避免故障的發生。
對失效數據進行定期清理
例如我們會在HASH結構中以增量的形式不斷寫入大量數據而忽略了這些數據的時效性,這些大量堆積的失效數據會造成大Key的產生,可以通過定時任務的方式對失效數據進行清理。在此類場景中,建議使用HSCAN並配合HDEL對失效數據進行清理,這種方式能夠在不阻塞的前提下清理無效數據。
使用阿里雲的Tair(Redis企業版)服務避開失效數據的清理工作
如果你的HASH Key過多,同時存在大量的成員失效需要被清理的問題。由於大量Key與大量失效數據的疊加,在此類場景中定時任務已無法做到對無效數據進行及時的清理,阿里雲的Tair服務能夠很好的解決此類問題。
Tair是阿里雲的Redis企業版,它在具備Redis所有特性(包括Redis的高性能特點)的同時提供了大量額外的高級功能。
TairHash是一種可為field設置過期時間和版本的hash類型數據結構,它不但和Redis Hash一樣支持豐富的數據接口和高處理性能,還改變了hash只能為key設置過期時間的限制:TairHash允許為field設置過期時間和版本。這極大地提高了hash數據結構的靈活性,簡化了很多場景下的業務開發工作。
TairHash使用高效的Active Expire算法,實現了在對響應時間幾乎無影響的前提下,高效完成對field過期判斷和刪除的功能。此類高級功能的合理使用能夠解放大量Redis的運維、故障處理工作並降低業務的代碼復雜度,讓運維將精力投入到其它更有價值的工作中,讓研發有更多的時間來寫更有價值的代碼。
2 熱Key的常見處理辦法
在Redis Cluster結構中對熱Key進行復制
在Redis Cluster中,熱Key由於遷移粒度問題造成請求無法打散使單一node的壓力無法下降。此時可以將對應熱Key進行復制並遷移至其他node,例如為熱Key foo復制出3個內容完全一樣的Key並名為foo2,foo3,foo4,然后將這三個Key遷移到其他node來解決單一node的熱Key壓力。
該方案的缺點在於代碼需要聯動修改,同時,Key一變多帶來了數據一致性挑戰:由更新一個Key演變為需要同時更新多個Key,在很多時候,該方案僅建議用來臨時解決當前的棘手問題。
使用讀寫分離架構
如果熱Key的產生來自於讀請求,那么讀寫分離是一個很好的解決方案。在使用讀寫分離架構時可以通過不斷的增加從節點來降低每個Redis實例中的讀請求壓力。
然而,讀寫分離架構在業務代碼復雜度增加的同時,同樣帶來了Redis集群架構復雜度的增加:我們不僅要為多個從節點提供轉發層(如Proxy,LVS等)來實現負載均衡,還要考慮從節點數量顯著增加后帶來的故障率增加的問題,Redis集群架構變更的同時為監控、運維、故障處理帶來了更大的挑戰。
但是,這一切在阿里雲Redis服務中顯得極為簡單,阿里雲Redis服務以開箱即用的方式提供服務。同時,在業務的發展發生變化時,阿里雲的Redis服務允許用戶通過變配的方式調整集群架構來輕松應對,如:主從轉變為讀寫分離,讀寫分構轉變為集群,主從轉變為支持讀寫分離的集群,以及由社區版直接轉變為支持大量高級特性的企業版Redis(Tair)。
讀寫分離架構同樣存在缺點,在請求量極大的場景下,讀寫分離架構會產生不可避免的延遲,此時會有讀取到臟數據的問題,因此,在讀寫壓力都較大寫對數據一致性要求很高的場景下,讀寫分離架構並不合適。
使用阿里雲Tair的QueryCache特性
QueryCache是阿里雲Tair(Redis企業版)服務的企業級特性之一,它的原理如下圖2-1:
圖2-1:Tair QueryCache原理
阿里雲數據庫Redis會根據高效的排序和統計算法識別出實例中存在的熱點Key,開啟該功能后,Proxy點會根據設定的規則緩存熱點Key的請求和查詢結果(僅緩存熱點Key的查詢結果,無需緩存整個Key),當在緩存有效時間內收到相同的請求時Proxy會直接返回結果至客戶端,無需和后端的Redis分片執行交互。在提升讀取速度的同時,降低了熱點Key對數據分片的性能影響,避免發生請求傾斜。
至此,來自客戶端的同樣的請求無需再與Proxy后端的Redis進行交互而由Proxy直接返回數據,指向熱Key的請求由一個Redis節點承擔轉為多個Proxy共同承擔,能夠大幅度降低Redis節點的熱Key壓力,同時Tair的QueryCache功能還提供了大量的命令來方便用戶查看、管理,如通過querycache keys命令查看所有被緩存熱Key,通過querycache listall獲取所有已緩存的所有命令等。
Tair QueryCache智能化的熱Key判定與緩存聯動功同樣能夠降低運維及研發的工作負擔。
與傳統的Redis同步中間件相比,阿里雲Redis全球分布式緩存具有高可靠性、高吞吐低延遲、同步正確性高等特點。
本文為阿里雲原創內容,未經允許不得轉載。