Redis數(shù)據(jù)庫的數(shù)據(jù)傾斜詳解
一、定義
引用百度百科的定義:
對于集群系統(tǒng),一般緩存是分布式的,即不同節(jié)點負責一定范圍的緩存數(shù)據(jù)。我們把緩存數(shù)據(jù)分散度不夠,導致大量的緩存數(shù)據(jù)集中到了一臺或者幾臺服務(wù)節(jié)點上,稱為數(shù)據(jù)傾斜。一般來說數(shù)據(jù)傾斜是由于負載均衡實施的效果不好引起的。
二、危害
如果發(fā)生了數(shù)據(jù)傾斜,那么就會有某一臺機器或幾臺保存了大量數(shù)據(jù)。輕則造成性能下降,處理請求速度驟降。重則造成Redis服務(wù)器崩潰,緩存服務(wù)不可用,將性能影響范圍擴散到DB層,對后端服務(wù)造成不可估量的后果。
三、數(shù)據(jù)傾斜的分類及應(yīng)對方案
1、寫入傾斜
示例
如圖,在某些情況下,實例上的數(shù)據(jù)分布不均衡,某個實例上的數(shù)據(jù)特別多。

分析及應(yīng)對方案
1、bigkey導致傾斜
bigkey指的是某個 Redis 實例上保存了一個很大的 value (String 類型)或者是大量的集合元素(集合類型)的 key ,這個 key 就被稱之為 bigkey ,而bigkey這種情況會導致集群中的某個實例的數(shù)據(jù)量很大,內(nèi)存資源消耗也相應(yīng)增加。
應(yīng)對方案
在業(yè)務(wù)層生成數(shù)據(jù)時,要盡量避免把過多的數(shù)據(jù)保存在同一個鍵值對中。如果 bigkey 正好是集合類型,還有一個方法,就是把 bigkey 拆分成很多個小的集合類型數(shù)據(jù),分散保存在不同的實例上。
2、Slot分配不均導致傾斜
介紹一下slot,slot全稱HashSlot(哈希槽),類似于數(shù)據(jù)分區(qū),每個key都會根據(jù)Hash算法計算出它應(yīng)該屬于哪個哈希槽,最終落到那個哈希槽中。而 Redis Cluster 就是采用哈希槽的方式來處理數(shù)據(jù)和實例間的映射關(guān)系。事實上,在 Redis Cluster 分片集群中一共有16384 個 Slot。
這里的Hash算法市面上的方式一般是先計算hash值,然后將計算結(jié)果對slot個數(shù)取模,最終確定落到哪個slot上。而計算hash值的算法有很多,常用的像CRC16、CRC64、sha1等等 運維在構(gòu)建切片集群時候,需要手動分配哈希槽,并且把16384 個槽都分配完,否則 Redis 集群無法正常工作。由于是手動分配,則可能會導致部分實例所分配的slot過多,導致數(shù)據(jù)傾斜。
應(yīng)對方案
使用CLUSTER SLOTS 命令來查看slot分配情況,使用CLUSTER SETSLOT,CLUSTER GETKEYSINSLOT,MIGRATE這三個命令來進行slot數(shù)據(jù)的遷移,具體內(nèi)容不再這里細說,感興趣的同學可以自行學習一下。
3.Hash Tag導致傾斜
Hash Tag 定義 :指當一個key包含 {} 的時候,就不對整個key做hash,而僅對 {} 包括的字符串做hash。假設(shè)hash算法為sha1。
對user:{user1}:ids和user:{user1}:tweets,其hash值都等同于sha1(user1)。
也就是說,如果不同 key 的 Hash Tag 內(nèi)容都是一樣的,那么,這些 key 對應(yīng)的數(shù)據(jù)會被映射到同一個 Slot 中,同時會被分配到同一個實例上。
所以,如果不合理使用Hash Tag,會導致大量的數(shù)據(jù)可能被集中到一個實例上發(fā)生數(shù)據(jù)傾斜,集群中的負載不均衡。
應(yīng)對方案
按照需求合理使用Hash Tag,甚至可以考量是否需要用到Hash Tag。
2、讀取傾斜(熱key)
示例
一般來說,讀取傾斜大多數(shù)都是熱key問題導致的。如圖所示,雖然每個集群實例上的數(shù)據(jù)量相差并沒有很大,但是如果其中某個實例上的數(shù)據(jù)是熱點數(shù)據(jù),那臺實例就會被訪問得非常頻繁。

產(chǎn)生熱key的原因及危害
原因:用戶消費的數(shù)據(jù)遠大于生產(chǎn)的數(shù)據(jù)(熱賣商品、熱點新聞、熱點評論、明星直播)。
在日常工作中一些突發(fā)的事件,例如:雙十一期間某些熱門商品在進行降價促銷或秒殺時,這時某一件商品會被數(shù)萬次點擊瀏覽或者購買,會形成一個較大的需求量,這種情況下就很容易造成熱點問題。
同理,被大量瀏覽的熱點數(shù)據(jù)、明星直播等,這些典型的讀多寫少的場景也會產(chǎn)生熱點問題。
危害:請求分片集中,超過單 Server 的性能極限。
在服務(wù)端讀數(shù)據(jù)訪問Redis時,往往會對請求key進行分片計算,此時中會將請求打到某一臺 Server 上,如果熱點過于集中,熱點 Key 的緩存過多,訪問量超過 Server 極限時,就會出現(xiàn)緩存分片服務(wù)被打垮現(xiàn)象的產(chǎn)生。當緩存服務(wù)崩潰后,此時再有請求產(chǎn)生,就會打到DB 上,這也就是我們常說的緩存穿透,如果沒有合理的解決,數(shù)據(jù)庫又沒有扛住大量的穿透請求,則會進一步導致數(shù)據(jù)庫雪崩現(xiàn)象。造成所有連接此數(shù)據(jù)庫的系統(tǒng)服務(wù)不可用,上下游調(diào)用鏈中斷,產(chǎn)生不可估量的后果。
分析及應(yīng)對方案:
① 拆分熱key
拆分熱key,指的是把熱點數(shù)據(jù)拆分成多份,在每份數(shù)據(jù)副本的 key 中增加一個隨機后綴,讓它和其它副本數(shù)據(jù)不會被映射到同一個 Slot 中。這里相當于把一份數(shù)據(jù)復(fù)制到多個實例上,通過Hash算法實現(xiàn)一個簡陋的負載均衡。同樣的,在讀取的時候也要增加隨機后綴,將對一個實例的讀取壓力,均攤到多個實例上。 例如:我們在放入緩存時就將對應(yīng)業(yè)務(wù)的緩存key拆分成多個不同的key。如下圖所示,在寫入緩存的過程中,我們首先將key拆成N份,比如某個請求進來的key名字叫做"hot_key",那我們就可以把它拆成“hot_key_001”、“hot_key_002”、“hot_key_003”、“hot_key_004”…,當然了,每次更新和新增時都要記得去改動這N個key,這就是拆key。

對于Service端來講,我們要盡可能的將訪問流量分流的足夠的均勻。 如何給即將訪問的熱key上合理的加入后綴?說一下市面上常用的方案,根據(jù)本機的ip或mac地址做hash,之后的值與拆key的數(shù)量做取余,最終決定拼接成什么樣的key后綴,從而打到哪臺機器上。當然也有其他的解決方案,比如在服務(wù)啟動時的一個隨機數(shù)對拆key的數(shù)量做取余。 偽代碼如下:
public boolean getRandomHotKey(String hotKey,int count) {
int random = new Random().nextInt(count);
randomKey = hotKey + "_" + random;
Object data = redis.get(randomKey);
if (data == null){
data = getFromDB();
redis.set(randomKey,expireTime + random);
}
}② 多級緩存+動態(tài)計算自動發(fā)現(xiàn)熱點緩存
基本流程圖

該方案主要是通過主動發(fā)現(xiàn)熱點并對其進行本地緩存來解決熱點 Key 的問題。對,你沒有聽錯,就是在緩存上再架設(shè)一層緩存。具體來說,就是在 Proxy上增加本地緩存,本地緩存采用LRU算法來緩存熱點數(shù)據(jù),后端節(jié)點增加熱點數(shù)據(jù)計算模塊來返回熱點數(shù)據(jù)。當然了,Client會訪問SLB,并且通過SLB將各種請求分發(fā)至Proxy中,Proxy會按照基于路由的方式將請求轉(zhuǎn)發(fā)至Redis中。
Proxy 架構(gòu)的主要有以下優(yōu)點:
- Proxy 本地緩存熱點,讀能力可水平擴展
- DB 節(jié)點定時計算熱點數(shù)據(jù)集合
- DB 反饋 Proxy 熱點數(shù)據(jù)
- 對客戶端完全透明,不需做任何兼容
熱點數(shù)據(jù)的發(fā)現(xiàn)與存儲
對于熱點數(shù)據(jù)的發(fā)現(xiàn),首先會在一個周期內(nèi)對 Key 進行請求統(tǒng)計,在達到請求量級后會對熱點 Key 進行熱點定位,并將所有的熱點 Key 放入一個小的 LRU 鏈表內(nèi),在通過 Proxy 請求進行訪問時,若 Redis 發(fā)現(xiàn)待訪點是一個熱點,就會進入一個反饋階段,同時對該數(shù)據(jù)進行標記。
可以使用一個etcd或者zk集群來存儲反饋的熱點數(shù)據(jù),然后本地所有節(jié)點監(jiān)聽該熱點數(shù)據(jù),進而加載到本地JVM緩存中。
熱點數(shù)據(jù)的獲取

在熱點 Key 的處理上主要分為寫入跟讀取兩種形式,在數(shù)據(jù)寫入過程當 SLB 收到數(shù)據(jù) Key1 并將其通過某一個 Proxy 寫入一個 Redis,完成數(shù)據(jù)的寫入。
假若經(jīng)過后端熱點模塊計算發(fā)現(xiàn) Key1 成為熱點 key 后, Proxy 會將該熱點進行本地緩存,當下次客戶端再進行訪問 Key1 時,則可以不讀取 Redis,直接從 Proxy 返回數(shù)據(jù)。
注意:由于 Proxy 是可以水平擴充的,因此可以任意增強熱點數(shù)據(jù)的訪問能力。
成熟方案: JD開源hotKey
上述的緩存傾斜解決思路,目前較為成熟解決方案是京東開源的項目HotKey,它擁有自動探測熱Key、分布式一致性緩存的設(shè)計。原理就是在Client端做洞察,然后上報對應(yīng)Hotkey,Server端檢測到后,將對應(yīng)Hotkey下發(fā)到對應(yīng)服務(wù)端做本地緩存,并且能保證本地緩存和遠程緩存的一致性。
到此這篇關(guān)于Redis數(shù)據(jù)庫的數(shù)據(jù)傾斜詳解的文章就介紹到這了,更多相關(guān)Redis數(shù)據(jù)傾斜內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在Ubuntu?14.04系統(tǒng)上備份和恢復(fù)Redis數(shù)據(jù)詳細步驟
這篇文章主要給大家介紹了關(guān)于在Ubuntu?14.04系統(tǒng)上備份和恢復(fù)Redis數(shù)據(jù)的詳細步驟,文中通過代碼介紹的非常詳細,對大家學習或者使用Redis具有一定的參考借鑒價值,需要的朋友可以參考下2024-04-04
CentOS 7下安裝 redis 3.0.6并配置集群的過程詳解
這篇文章主要給大家介紹了CentOS 7下安裝 redis 3.0.6并配置集群的過程,文中通過示例代碼和詳細的步驟介紹的很相信,對大家具有一定的參考價值,有需要的朋友們下面來一起看看吧。2017-01-01
Redis+Caffeine實現(xiàn)高效兩級緩存架構(gòu)的詳細指南
在現(xiàn)代高并發(fā)系統(tǒng)中,緩存是提升系統(tǒng)性能的關(guān)鍵組件之一,本文將介紹如何結(jié)合 Redis 和 Caffeine 構(gòu)建一個高效的兩級緩存系統(tǒng),需要的小伙伴可以了解下2025-07-07
Redis緩沖區(qū)溢出是指Redis緩沖區(qū)被寫入的數(shù)據(jù)超過了它的容量,導致數(shù)據(jù)無法存儲或被覆蓋。造成緩沖區(qū)溢出的原因可能是快速寫入大量數(shù)據(jù)、緩沖區(qū)未及時刷新或Redis服務(wù)器配置不當?shù)取?/div> 2023-04-04
Redis中Key過期時間的設(shè)置與應(yīng)用方式
文章主要介紹了Redis中設(shè)置和管理Key過期時間的多種命令,包括EXPIRE、PEXPIRE、EXPIREAT、PEXPIREAT、SET命令結(jié)合EX或PX參數(shù)、SETEX命令,以及獲取剩余過期時間的TTL和PTTL命令,最后通過緩存、會話管理和分布式鎖等應(yīng)用場景展示了這些命令的實際應(yīng)用2025-11-11
Linux服務(wù)器使用Redis作為數(shù)據(jù)緩存并用log4j2進行日志記錄的過程分享
這篇文章主要介紹了Linux服務(wù)器使用Redis作為數(shù)據(jù)緩存并用log4j2日志記錄,關(guān)于SpringBoot項目配置Redis與log4j2是查詢官方文檔,本文中的Redis配置類、Redis工具類以及l(fā)og4j2.xml配置文件來自網(wǎng)絡(luò),查證源自何處比較麻煩,所以在此感謝所有人的分享2023-09-09最新評論

