聊聊Redis中如何應對快取熱key問題?常用方案分享

2022-02-10 19:00:33
Redis中如何應對熱key問題?下面本篇文章就來給大家介紹一下Redis快取熱key問題的常用解決方案,希望對大家有所幫助!

做一些C端業務,不可避免的要引入一級快取來代替資料庫的壓力並且減少業務響應時間,其實每次引入一箇中介軟體來解決問題的同時,必然會帶來很多新的問題需要注意,比如上篇文章《資料庫與快取一致性實戰》中提到的如何做快取的一致性。那麼其實還會有一些其他問題比如使用Redis作為一級快取時可能帶來的熱key、大key等問題,本文我們就熱key(hot key)問題來討論,如何合理的解決熱key問題。

背景

熱key是什麼問題,如何導致的?

一般來說,我們使用的快取Redis都是多節點的叢集版,對某個key進行讀寫時,會根據該key的hash計算出對應的slot,根據這個slot就能找到與之對應的分片(一個master和多個slave組成的一組redis叢集)來存取該K-V。但是在實際應用過程中,對於某些特定業務或者一些特定的時段(比如電商業務的商品秒殺活動),可能會發生大量的請求存取同一個key。所有的請求(且這類請求讀寫比例非常高)都會落到同一個redis server上,該redis的負載就會嚴重加劇,此時整個系統增加新redis範例也沒有任何用處,因為根據hash演演算法,同一個key的請求還是會落到同一臺新機器上,該機器依然會成為系統瓶頸2,甚至造成整個叢集宕掉,若此熱點key的value 也比較大,也會造成網路卡達到瓶頸,這種問題稱為 「熱key」 問題。【相關推薦:Redis視訊教學

如下圖1、2所示,分別是正常redis cluster叢集和使用一層proxy代理的redis 叢集key存取。

1.png

2.png

如上所說,熱key會給叢集中的少部分節點帶來超高的負載壓力,如果不正確處理,那麼這些節點宕機都有可能,從而會影響整個快取叢集的運作,因此我們必須及時發現熱key、解決熱key問題。

1.熱key探測

熱key探測,看到由於redis叢集的分散性以及熱點key帶來的一些顯著影響,我們可以通過由粗及細的思考流程來做熱點key探測的方案。

1.1 叢集中每個slot的qps監控

熱key最明顯的影響是整個redis叢集中的qps並沒有那麼大的前提下,流量分佈在叢集中slot不均的問題,那麼我們可以最先想到的就是對於每個slot中的流量做監控,上報之後做每個slot的流量對比,就能在熱key出現時發現影響到的具體slot。雖然這個監控最為方便,但是粒度過於粗了,僅適用於前期叢集監控方案,並不適用於精準探測到熱key的場景。

1.2 proxy的代理機制作為整個流量入口統計

如果我們使用的是圖2的redis叢集proxy代理模式,由於所有的請求都會先到proxy再到具體的slot節點,那麼這個熱點key的探測統計就可以放在proxy中做,在proxy中基於時間滑動視窗,對每個key做計數,然後統計出超出對應閾值的key。為了防止過多冗餘的統計,還可以設定一些規則,僅統計對應字首和型別的key。這種方式需要至少有proxy的代理機制,對於redis架構有要求。

1.3 redis基於LFU的熱點key發現機制

redis 4.0以上的版本支援了每個節點上的基於LFU的熱點key發現機制,使用redis-cli –hotkeys即可,執行redis-cli時加上–hotkeys選項。可以定時在節點中使用該命令來發現對應熱點key。

3.png

如下所示,可以看到redis-cli –hotkeys的執行結果,熱key的統計資訊,這個命令的執行時間較長,可以設定定時執行來統計。

1.4 基於Redis使用者端做探測

由於redis的命令每次都是從使用者端發出,基於此我們可以在redis client的一些程式碼處進行統計計數,每個client做基於時間滑動視窗的統計,超過一定的閾值之後上報至server,然後統一由server下發至各個client,並且設定對應的過期時間。

這個方式看起來更優美,其實在一些應用場景中並不是那麼合適,因為在client端這一側的改造,會給執行的程序帶來更大的記憶體開銷,更直接的來說,對於Java和goLang這種自動記憶體管理的語言,會更加頻繁的建立物件,從而觸發gc導致介面響應耗時增加的問題,這個反而是不太容易預料到的事情。

最終可以通過各個公司的基建,做出對應的選擇。

2.熱key解決

通過上述幾種方式我們探測到了對應熱key或者熱slot,那麼我們就要解決對應的熱key問題。解決熱key也有好幾種思路可以參考,我們一個一個捋一下。

2.1 對特定key或slot做限流

一種最簡單粗暴的方式,對於特定的slot或者熱key做限流,這個方案明顯對於業務來說是有損的,所以建議只用在出現線上問題,需要止損的時候進行特定的限流。

2.2 使用二級(本地)快取

本地快取也是一個最常用的解決方案,既然我們的一級快取扛不住這麼大的壓力,就再加一個二級快取吧。由於每個請求都是由service發出的,這個二級快取加在service端是再合適不過了,因此可以在伺服器端每次獲取到對應熱key時,使用本地快取儲存一份,等本地快取過期後再重新請求,降低redis叢集壓力。以java為例,guavaCache就是現成的工具。以下範例:

    //本地快取初始化以及構造
    private static LoadingCache<String, List<Object>> configCache
            = CacheBuilder.newBuilder()
            .concurrencyLevel(8)  //並行讀寫的級別,建議設定cpu核數
            .expireAfterWrite(10, TimeUnit.SECONDS)  //寫入資料後多久過期
            .initialCapacity(10) //初始化cache的容器大小
            .maximumSize(10)//cache的容器最大
            .recordStats()
            // build方法中可以指定CacheLoader,在快取不存在時通過CacheLoader的實現自動載入快取
            .build(new CacheLoader<String, List<Object>>() {
                @Override
                public List<Object> load(String hotKey) throws Exception {
                    
                }
            });
    
    //本地快取獲取
    Object result = configCache.get(key);

本地快取對於我們的最大的影響就是資料不一致的問題,我們設定多長的快取過期時間,就會導致最長有多久的線上資料不一致問題,這個快取時間需要衡量自身的叢集壓力以及業務接受的最大不一致時間。

2.3 拆key

如何既能保證不出現熱key問題,又能儘量的保證資料一致性呢?拆key也是一個好的解決方案。

我們在放入快取時就將對應業務的快取key拆分成多個不同的key。如下圖所示,我們首先在更新快取的一側,將key拆成N份,比如一個key名字叫做"good_100",那我們就可以把它拆成四份,"good_100_copy1"、"good_100_copy2"、"good_100_copy3"、"good_100_copy4",每次更新和新增時都需要去改動這N個key,這一步就是拆key。

對於service端來講,我們就需要想辦法儘量將自己存取的流量足夠的均勻,如何給自己即將存取的熱key上加入字尾。幾種辦法,根據本機的ip或mac地址做hash,之後的值與拆key的數量做取餘,最終決定拼接成什麼樣的key字尾,從而打到哪臺機器上;服務啟動時的一個亂數對拆key的數量做取餘。

4.png

2.4 本地快取的另外一種思路 設定中心

對於熟悉微服務設定中心的夥伴來講,我們的思路可以向設定中心的一致性轉變一下。拿nacos來舉例,它是如何做到分散式的設定一致性的,並且相應速度很快?那我們可以將快取類比設定,這樣去做。

長輪詢+在地化的設定。首先服務啟動時會初始化全部的設定,然後定時啟動長輪詢去查詢當前服務監聽的設定有沒有變更,如果有變更,長輪詢的請求便會立刻返回,更新本地設定;如果沒有變更,對於所有的業務程式碼都是使用原生的記憶體快取設定。這樣就能保證分散式的快取設定時效性與一致性。

2.5 其他可以提前做的預案

上面的每一個方案都相對獨立的去解決熱key問題,那麼如果我們真的在面臨業務訴求時,其實會有很長的時間來考慮整體的方案設計。一些極端的秒殺場景帶來的熱key問題,如果我們預算充足,可以直接做服務的業務隔離、redis快取叢集的隔離,避免影響到正常業務的同時,也會可以臨時採取更好的容災、限流措施。

一些整合的方案

目前市面上已經有了不少關於hotKey相對完整的應用級解決方案,其中京東在這方面有開源的hotkey工具,原理就是在client端做洞察,然後上報對應hotkey,server端檢測到後,將對應hotkey下發到對應伺服器端做本地快取,並且這個本地快取在遠端對應的key更新後,會同步更新,已經是目前較為成熟的自動探測熱key、分散式一致性快取解決方案,京東零售熱key

5.png

總結

以上就是筆者大概瞭解或實踐過的的如何應對熱key的一些方案,從發現熱key到解決熱key的兩個關鍵問題的應對。每一個方案都有優缺點,比如會帶來業務的不一致性,實施起來較為困難等等,可以根據目前自身業務的特點、以及目前公司的基建去做對應的調整和改變。

更多程式設計相關知識,請存取:!!

以上就是聊聊Redis中如何應對快取熱key問題?常用方案分享的詳細內容,更多請關注TW511.COM其它相關文章!