何為熱key問題?
從名字上可以理解,Redis中的熱key就是在Redis中頻繁被訪問的數據,例如熱點新聞,熱點評論,雙十一商品等等。當某一熱key的請求到Server主機時,這樣會造成流量過於集中,達到物理網卡上限,從而導致這台redis的主機資源不足,甚至宕機。那接下來這個key的請求,就會直接懟到你的數據庫上,導致你的服務不可用。
熱點數據對服務器來說,是一個巨大的隱患,以redis-cluster 來說,它可能會造成整體流量不均衡,個別ops過大的情況,極端的情況下,可能直接超過redis本事的承受能力。
熱key問題產生的原因
- 用戶消費的數據遠遠大於生產的數據,比如熱賣商品、熱點新聞、熱點評論等,這些典型的讀多寫少的場景會產生熱點問題。
2.在請求分片集中,超過單Server的性能極限,比如固定名稱的key,hash落入一台Server,在訪問量極大的情況下,超過Server極限時,就會導致熱點問題的產生。
熱key問題的危害
- 流量過於集中,達到物理網卡上限,從而導致這台redis的主機資源不足,甚至宕機
- 請求過多,緩存分片服務被打垮,不能通過擴容解決,且不能發揮集群多分片的優勢
- 可能會造成緩存雪崩和緩存穿透
如何發現熱key?
-
客戶端代碼統計
即在操作redis之前,加入一行代碼進行數據統計。這個數據統計的方式有多種,也可以是給外部的通訊系統發送一個通知信息。但是缺點是有代碼入侵,且維護成本較高,大規模落地肯定不合適,萬一熱key多了,還有可能oom。 -
憑借業務經驗,進行預估
例如提前知道了某個活動的開啟,那么就將此Key作為熱點Key。但不是每個人都有這個經驗的。 -
如果服務端有代理層,可以在代理層進行收集上報
在代理層做收集上報的話,首先,得用了代理,例如codis之類的。因此缺點也很明顯,並非所有的redis集群架構都有proxy,而且需要做二次開發,肯定得投入人力改proxy源碼,不論難不難,都要考慮一個穩定性和維護成本。 -
用redis自帶命令
- hotkeys參數,redis於4.0.3版本開始正式支持基於LFU的熱點key發現機制,執行redis-cli時加上–hotkeys選項即可。但是該參數在執行的時候,如果key比較多,執行起來比較慢,且只適用於緩存淘汰策略是lfu的時候。
(可以參考https://yq.aliyun.com/articles/278922) - monitor命令,該命令可以實時抓取出redis服務器接收到的命令,然后寫代碼統計出熱key是啥。當然,也有現成的分析工具可以給你使用,比如redis-faina。但是monitor命令在高並發的場景下,會存在內存暴增,影響redis的性能,只適合短時間的統計,不適合一直使用,只能統計單節點的熱點key,對於集群需要進行匯總統計。
- hotkeys參數,redis於4.0.3版本開始正式支持基於LFU的熱點key發現機制,執行redis-cli時加上–hotkeys選項即可。但是該參數在執行的時候,如果key比較多,執行起來比較慢,且只適用於緩存淘汰策略是lfu的時候。
-
TCP消息抓包
Redis客戶端使用TCP協議與服務端進行交互,通信協議采用的是RESP。如果站在機器的角度,可以通過對機器上所有Redis端口的TCP數據包進行抓取完成熱點key的統計。但是依然存在3個問題:- 需要一定的開發成本,但是一些開源方案實現了該功能,例如ELK(ElasticSearch Logstash Kibana)體系下的packetbeat[2] 插件,可以實現對Redis、MySQL等眾多主流服務的數據包抓取、分析、報表展示;
- 對於高流量的機器抓包,對機器網絡可能會有干擾,同時抓包時候會有丟包的可能性;
- 維護成本過高。
如何解決熱key問題?
- 拆分復雜數據結構: 如果當前key的類型是一個二級數據結構,例如哈希類型。如果該哈希元素個數較多,可以考慮將當前hash進行拆分,這樣該熱點key可以拆分為若干個新的key分布到不同Redis節點上,從而減輕壓力。
- 備份熱點key:不要讓key走到同一台redis上就行了,把熱點key在多個redis上都存一份,有熱key請求進來的時候,我們就在有備份的redis上隨機選取一台,進行訪問取值,返回數據。
假設redis的集群數量為N,步驟如下圖所示:
不一定是2N,你想取3N,4N都可以,看要求。 - 本地緩存加通知機制:可以將熱點key放在業務端的本地緩存中,針對這種熱key請求,會直接從JVM中去,而不通過redis層。因為是在業務端的本地內存中,處理能力要高出Redis數十倍,但當數據更新時,此種模式會造成各個業務端和Redis數據不一致,通常會使用發布訂閱機制來解決類似問題。
(亦可參考https://www.iteye.com/blog/carlosfu-2269687)