Redis 切片集群的數據傾斜分析

Redis 中如何應對數據傾斜

什麼是數據傾斜

如果 Redis 中的部署,採用的是切片集群,數據是會按照一定的規則分散到不同的實例中保存,比如,使用 Redis ClusterCodis

數據傾斜會有下面兩種情況:

1、數據量傾斜:在某些情況下,實例上的數據分布不均衡,某個實例上的數據特別多。

2、數據訪問傾斜:雖然每個集群實例上的數據量相差不大,但是某個實例上的數據是熱點數據,被訪問得非常頻繁。

發生了數據傾斜,會造成那些數據量大的和訪問高的實例節點,系統的負載升高,響應速度變慢。嚴重的情況造成記憶體資源耗盡,引起系統崩潰。

數據量傾斜

數據量傾斜,也就是實例上的數據分布不均衡,某個實例中的數據分布的特別多 。

數據量的傾斜,主要有下面三種情況:

1、bigkey導致傾斜;

2、Slot分配不均衡導致傾斜;

3、Hash Tag導致傾斜。

下面來一一的分析下

bigkey導致傾斜

什麼是 bigkey:我們將含有較大數據或含有大量成員、列表數的 Key 稱之為大Key。

  • 一個 STRING 類型的 Key,它的值為 5MB(數據過大)

  • 一個 LIST 類型的 Key,它的列表數量為 20000 個(列表數量過多)

  • 一個 ZSET 類型的 Key,它的成員數量為 10000 個(成員數量過多)

  • 一個 HASH 格式的 Key,它的成員數量雖然只有 1000 個但這些成員的 value 總大小為 100MB(成員體積過大)

如果某個實例中保存了 bigkey,那麼就有可能導致集群的數據傾斜。

bigkey 存在問題

  • 記憶體空間不均勻:如果採用切片集群的部署方案,容易造成某些實例節點的記憶體分配不均勻;

  • 造成網路擁塞:讀取 bigkey 意味著需要消耗更多的網路流量,可能會對 Redis 伺服器造成影響;

  • 過期刪除:bigkey 不單讀寫慢,刪除也慢,刪除過期 bigkey 也比較耗時;

  • 遷移困難:由於數據龐大,備份和還原也容易造成阻塞,操作失敗;

如何避免

對於bigkey可以從以下兩個方面進行處理

1、合理優化數據結構

  • 1、對較大的數據進行壓縮處理;

  • 2、拆分集合:將大的集合拆分成小集合(如以時間進行分片)或者單個的數據。

2、選擇其他的技術來存儲 bigkey

  • 使用其他的存儲形式,考慮使用 cdn 或者文檔性資料庫 MongoDB。

Slot分配不均衡導致傾斜

例如在 Redis Cluster 通過 Slot 來給數據分配實例

1、Redis Cluster方案採用哈希槽來處理 KEY 在不同實例中的分布,一個切片集群共有 16384 個哈希槽,這些哈希槽類似於數據分區,每個鍵值對都會根據它的 key,被映射到一個哈希槽中;

2、一個 KEY ,首先會根據 CRC16 演算法計算一個16 bit的值;然後,再用這個 16bit 值對 16384 取模,得到 0~16383 範圍內的模數,每個模數代表一個相應編號的哈希槽。

3、然後把哈希槽分配到所有的實例中,例如,如果集群中有N個實例,那麼,每個實例上的槽個數為16384/N個。

如果 Slot 分配的不均衡,就會導致某幾個實例中數據量偏大,進而導致數據傾斜的發生。

出現這種問題,我們就可以使用遷移命令把這些 Slot 遷移到其它實例上,即可。

Hash Tag導致傾斜

Hash Tag 用於 redis 集群中,其作用是將具有某一固定特徵的數據存儲到同一台實例上。其實現方式為在 key 中加個 {},例如 test{1}

使用 Hash Tag 後客戶端在計算 key 的 crc16 時,只計算 {} 中數據。如果沒使用 Hash Tag,客戶端會對整個 key 進行 crc16 計算。

數據key 哈希計算 對應的Slot
user:info:{3231} CRC16(‘3231’) mod 16384 1024
user:info:{5328} CRC16(‘5328’) mod 16384 3210
user:order:{3231} CRC16(‘3231’) mod 16384 1024
user:order:{5328} CRC16(‘5328’) mod 16384 3210

這樣通過 Hash Tag 就可以將某一固定特徵數據存儲到一台實例上,避免逐個查詢集群中實例。

栗如:如果我們進行事務操作或者數據的範圍查詢,因為Redis Cluster和 Codis 本身並不支援跨實例的事務操作和範圍查詢,當業務應用有這些需求時,就只能先把這些數據讀取到業務層進行事務處理,或者是逐個查詢每個實例,得到範圍查詢的結果。

Hash Tag潛在的問題就是,可能存在大量數據被映射到同一個實例的情況出現,導致集群的數據傾斜,集群中的負載不均衡。

所有當我使用 Hash Tag 的時候就做好評估,我們的業務訴求如果不使用 Hash Tag 可以解決嗎,如果不可避免的使用,我們需要評估好數據量,盡量避免數據傾斜的出現。

數據訪問傾斜

雖然每個集群實例上的數據量相差不大,但是某個實例上的數據是熱點數據,被訪問得非常頻繁,這就是數據訪問傾斜。

數據量訪問傾斜的罪魁禍首就是 Hot Key

切片集群中的 Key 最終會存儲到集群中的一個固定的 Redis 實例中。某一個 Key 在一段時間內訪問遠高於其它的 Key,也就是該 Key 對應的 Redis 實例,會收到過大的流量請求,該實例容易出現過載和卡頓現象,甚至還會被打掛掉。

常見引發熱點 Key 的情況:

1、新聞中的熱點事件;

2、秒殺活動中的,性價比高的商品;

如何發現 Hot Key
  • 1、提現預判;

根據業務經驗進行提前預判;

  • 2、在客戶端進行收集;

通過在客戶端增加命令的採集,來統計發現熱點 Key;

  • 3、使用 Redis 自帶的命令排查;

使用monitor命令統計熱點key(不推薦,高並發條件下會有造成redis 記憶體爆掉的隱患);

hotkeys參數,redis 4.0.3提供了redis-cli的熱點key發現功能,執行redis-cli時加上–hotkeys選項即可。但是該參數在執行的時候,如果key比較多,執行起來比較慢。

  • 4、在Proxy層做收集

如果集群架構引入了 proxy,可以在 proxy 中做統計

  • 5、自己抓包評估

Redis客戶端使用TCP協議與服務端進行交互,通訊協議採用的是RESP。自己寫程式監聽埠,按照RESP協議規則解析數據,進行分析。缺點就是開發成本高,維護困難,有丟包可能性。

Hot Key 如何解決

知道了Hot Key如何來應對呢

  • 1、對 Key 進行分散處理;

舉個栗子

有一個熱 Key 名字為Hot-key-test,可以將其分散為Hot-key-test1Hot-key-test2…然後將這些 Key 分散到多個實例節點中,當客戶端進行訪問的時候,隨機一個下標的 Key 進行訪問,這樣就能將流量分散到不同的實例中了,避免了一個快取節點的過載。

一般來講,可以通過添加後綴或者前綴,把一個 hotkey 的數量變成 redis 實例個數 N 的倍數 M,從而由訪問一個redis key變成訪問N * M個redis key。 N*Mredis key經過分片分布到不同的實例上,將訪問量均攤到所有實例。

const M = N * 2
//生成隨機數
random = GenRandom(0, M)
//構造備份新key
bakHotKey = hotKey + 「_」 + random
data = redis.GET(bakHotKey)
if data == NULL {
    data = GetFromDB()
    redis.SET(bakHotKey, expireTime + GenRandom(0,5))
}
  • 2、使用本地快取;

業務端還可以使用本地快取,將這些熱 key 記錄在本地快取,來減少對遠程快取的衝擊。

這裡,有個地方需要注意下,熱點數據多副本方法只能針對只讀的熱點數據。如果熱點數據是有讀有寫的話,就不適合採用多副本方法了,因為要保證多副本間的數據一致性,會帶來額外的開銷。

對於有讀有寫的熱點數據,我們就要給實例本身增加資源了,例如使用配置更高的機器,來應對大量的訪問壓力。

總結

1、數據傾斜會有下面兩種情況;

  • 1、數據量傾斜:在某些情況下,實例上的數據分布不均衡,某個實例上的數據特別多。

  • 2、數據訪問傾斜:雖然每個集群實例上的數據量相差不大,但是某個實例上的數據是熱點數據,被訪問得非常頻繁。

2、數據量的傾斜,主要有下面三種情況;

  • 1、bigkey導致傾斜;

  • 2、Slot分配不均衡導致傾斜;

  • 3、Hash Tag導致傾斜。

3、數據訪問傾斜,原因就是 Hot Key 造成的,出現Hot Key,一般下面有下面兩種方式去解決;

  • 1、對 Key 進行分散處理;

  • 2、使用本地快取;

參考

【Redis核心技術與實戰】//time.geekbang.org/column/intro/100056701
【Redis設計與實現】//book.douban.com/subject/25900156/
【Redis 的學習筆記】//github.com/boilingfrog/Go-POINT/tree/master/redis
【Redis中的切片集群】//boilingfrog.github.io/2022/02/20/redis中常見的集群部署方案/#切片集群
【Redis 切片集群的數據傾斜分析】//boilingfrog.github.io/2022/06/22/Redis切片集群的數據傾斜分析/

Tags: