1 數(shù)據(jù)傾斜
1.1 定義
對于集群系統(tǒng)劲装,一般緩存是分布式的经备,即不同節(jié)點(diǎn)負(fù)責(zé)一定范圍的緩存數(shù)據(jù)阻问。我們把緩存數(shù)據(jù)分散度不夠梧税,導(dǎo)致大量的緩存數(shù)據(jù)集中到了一臺或者幾臺服務(wù)節(jié)點(diǎn)上,稱為數(shù)據(jù)傾斜称近。一般來說數(shù)據(jù)傾斜是由于負(fù)載均衡實(shí)施的效果不好引起的第队。
1.2 危害
如果發(fā)生了數(shù)據(jù)傾斜,那么保存了大量數(shù)據(jù)刨秆,或者是保存了熱點(diǎn)數(shù)據(jù)的實(shí)例的處理壓力就會增大凳谦,速度變慢,甚至還可能會引起這個(gè)實(shí)例的內(nèi)存資源耗盡衡未,從而崩潰尸执。這是我們在應(yīng)用切片集群時(shí)要避免的家凯。
1.3 分類
-
數(shù)據(jù)量傾斜(bigkey導(dǎo)致傾斜)
某個(gè)實(shí)例上正好保存了 bigkey。bigkey 的 value 值很大(String 類型)如失,或者是 bigkey 保存了大量集合元素(集合類型)绊诲,會導(dǎo)致這個(gè)實(shí)例的數(shù)據(jù)量增加,內(nèi)存資源消耗也相應(yīng)增加褪贵。
-
數(shù)據(jù)訪問傾斜(讀取傾斜-熱key問題)
雖然每個(gè)集群實(shí)例上的數(shù)據(jù)量相差不大掂之,但是某個(gè)實(shí)例上的數(shù)據(jù)是熱點(diǎn)數(shù)據(jù),被訪問得非常頻繁
在 Redis(五):常見面試題目詳解 中寫過關(guān)于bigkey和hotkey的生產(chǎn)問題脆丁,但是只治標(biāo)不治本板惑,數(shù)據(jù)傾斜還是需要監(jiān)控起來,這樣在生成環(huán)境遇到問題時(shí)偎快,才不至于措手不及冯乘。
2 BigKey
2.1 定義
通常以Key的大小和Key中成員的數(shù)量來綜合判定,例如:
Key本身的數(shù)據(jù)量過大:一個(gè)String類型的Key晒夹,它的值為5 MB裆馒。
Key中的成員數(shù)過多:一個(gè)ZSET類型的Key,它的成員數(shù)量為10,000個(gè)丐怯。
Key中成員的數(shù)據(jù)量過大:一個(gè)Hash類型的Key喷好,它的成員數(shù)量雖然只有1,000個(gè)但這些成員的Value(值)總大小為100 MB。
2.2 產(chǎn)生原因
在不適用的場景下使用Redis读跷,易造成Key的value過大梗搅,如使用String類型的Key存放大體積二進(jìn)制文件型數(shù)據(jù);
業(yè)務(wù)上線前規(guī)劃設(shè)計(jì)不足效览,沒有對Key中的成員進(jìn)行合理的拆分无切,造成個(gè)別Key中的成員數(shù)量過多;
未定期清理無效數(shù)據(jù)丐枉,造成如HASH類型Key中的成員持續(xù)不斷地增加哆键;
使用LIST類型Key的業(yè)務(wù)消費(fèi)側(cè)發(fā)生代碼故障,造成對應(yīng)Key的成員只增不減瘦锹。
bigkey的產(chǎn)生主要是由于程序的設(shè)計(jì)不當(dāng)所造成的籍嘹,如以下幾種常見的業(yè)務(wù)場景
社交類:粉絲列表,如果某些明星或者大v不精心設(shè)計(jì)下弯院,必是bigkey辱士。
統(tǒng)計(jì)類:例如按天存儲某項(xiàng)功能或者網(wǎng)站的用戶集合,除非沒幾個(gè)人用听绳,否則必是bigkey颂碘。
緩存類:將數(shù)據(jù)從數(shù)據(jù)庫load出來序列化放到redis里,這個(gè)方式經(jīng)常常用辫红,但有兩個(gè)地方需要注意:第一凭涂,是不是有必要把所有字段都緩存;第二贴妻,有沒有相關(guān)關(guān)聯(lián)的數(shù)據(jù)切油。
由此可見,在程序設(shè)計(jì)中名惩,我們要對數(shù)據(jù)量的增長和邊界有一個(gè)基本性的評估澎胡,做好技術(shù)選型和技術(shù)架構(gòu)。
2.3 危害
-
1.內(nèi)存空間不均勻
在集群模式中娩鹉,由于bigkey的存在攻谁,會造成主機(jī)節(jié)點(diǎn)的內(nèi)存不均勻,這樣會不利于集群對內(nèi)存的統(tǒng)一管理弯予,存在丟失數(shù)據(jù)的隱患戚宦。
-
2.超時(shí)阻塞
由于redis單線程的特性,操作bigkey通常比較耗時(shí)锈嫩,也就意味著阻塞redis可能性越大受楼,這樣會造成客戶端阻塞或者引起故障切換。慢查詢通常就會有它們的身影呼寸。
-
3.網(wǎng)絡(luò)擁塞
bigkey也就意味著每次獲取要產(chǎn)生的網(wǎng)絡(luò)流量較大艳汽。假設(shè)一個(gè)bigkey為1MB,客戶端每秒訪問量為1000对雪,那么每秒產(chǎn)生1000MB的流量河狐,對于普通的千兆網(wǎng)卡(按照字節(jié)算是128MB/s)的服務(wù)器來說簡直是滅頂之災(zāi)。
-
4.阻塞刪除
有個(gè)bigkey瑟捣,對它設(shè)置了過期時(shí)間馋艺,當(dāng)它過期后會被刪除,如果使用Redis 4.0之前的版本迈套,過期key是同步刪除丈钙,就會存在阻塞redis的可能性,而且這個(gè)過期刪除不會從慢查詢發(fā)現(xiàn)(因?yàn)檫@個(gè)刪除不是客戶端產(chǎn)生的交汤,是內(nèi)部循環(huán)事件)雏赦。4.0之后的版本會采用異步刪除。
2.4 優(yōu)化方案
2.4.1 拆分Bigkey
優(yōu)化Bigkey的原則就是string減少字符串長度芙扎,list星岗、hash、set戒洼、zset等減少元素?cái)?shù)量俏橘。當(dāng)我們知道哪些key是Bigkey時(shí),可以把單個(gè)key拆分成多個(gè)key圈浇,比如以下拆分方式可以參考寥掐。
big list:list1靴寂、list2、...listN
big hash:可以做二次的hash召耘,例如hash%100
按照日期拆分多個(gè):key20220310百炬、key20220311、key202203212
如果bigkey不可避免污它,也要思考一下要不要每次把所有元素都取出來(例如有時(shí)候僅僅需要hmget剖踊,而不是hgetall),刪除也是一樣衫贬,盡量使用優(yōu)雅的方式來處理德澈。
2.4.2 選擇適合的數(shù)據(jù)類型。
例如:實(shí)體類型(要合理控制和使用數(shù)據(jù)結(jié)構(gòu)固惯,但也要注意節(jié)省內(nèi)存和性能之間的平衡)
反例:
set user:1:name tom
set user:1:age 19
set user:1:favor football </pre>
正例:
hmset user:1 name tom age 19 favor football
2.4.3 控制key的生命周期梆造,redis不是垃圾桶
建議使用expire設(shè)置過期時(shí)間(條件允許可以打散過期時(shí)間,防止集中過期)葬毫。
2.5 監(jiān)控并預(yù)警
Bigkey首先需要重源頭治理澳窑,防止Bigkey的產(chǎn)生;其次是需要能夠及時(shí)的發(fā)現(xiàn)供常,發(fā)現(xiàn)后及時(shí)處理摊聋。分析Bigkey的方法不少,這里介紹兩種比較常用的方法栈暇,也是Daas平臺分析Bigkey使用的兩種方式麻裁,分別是Bigkeys命令分析法、RDB文件分析法源祈。
2.5.1 scan命令分析
Redis4.0及以上版本提供了--Bigkeys命令皇耗,可以分析出實(shí)例中每種數(shù)據(jù)結(jié)構(gòu)的top 1的Bigkey桩皿,同時(shí)給出了每種數(shù)據(jù)類型的鍵值個(gè)數(shù)以及平均大小蚀狰。執(zhí)行--Bigkeys命令時(shí)候需要注意以下幾點(diǎn):
bigkeys 是以 scan 延遲計(jì)算的方式掃描所有 key苫幢,因此執(zhí)行過程中不會阻塞 redis,但實(shí)例存在大量的 keys 時(shí)图张,命令執(zhí)行的時(shí)間會很長锋拖,這種情況建議在 slave 上掃描。
建議在節(jié)點(diǎn)本機(jī)執(zhí)行祸轮,這樣可以減少網(wǎng)絡(luò)開銷兽埃。
如果沒有從節(jié)點(diǎn),可以使用--i參數(shù)适袜,例如(--i 0.1 代表100毫秒執(zhí)行一次)柄错。
--Bigkeys只能計(jì)算每種數(shù)據(jù)結(jié)構(gòu)的top1,如果有些數(shù)據(jù)結(jié)構(gòu)有比較多的Bigkey,是查找不出來的售貌。
string 類型統(tǒng)計(jì)的是 value 的字節(jié)數(shù)给猾;另外 4 種復(fù)雜結(jié)構(gòu)的類型統(tǒng)計(jì)的是元素個(gè)數(shù),不能直觀的看出 value 占用字節(jié)數(shù)(元素個(gè)數(shù)少颂跨,不一定 value 不大敢伸;元素個(gè)數(shù)多,也不一定 value 就大)毫捣,所以 --bigkeys 對分析 string 類型的大 key 是有用的,而復(fù)雜結(jié)構(gòu)的類型還需要一些第三方工具帝际。
redis-cli -h 127.0.0.1 -p 6379 -a "password" --bigkeys
2.5.2 RDB文件分析
借助開源的工具蔓同,比如rdb-tools,分析Redis實(shí)例的RDB文件蹲诀,找出其中的Bigkey斑粱,這種方式需要生成RDB文件,需要注意以下幾點(diǎn):
建議在slave節(jié)點(diǎn)執(zhí)行脯爪,因?yàn)樯蒖DB文件會影響節(jié)點(diǎn)性能则北。
需要生成RDB文件,會影響節(jié)點(diǎn)性能痕慢,雖然在slave節(jié)點(diǎn)執(zhí)行尚揣,但是也是有可能造成主從中斷,進(jìn)而影響到master節(jié)點(diǎn)掖举。
Rdbtools 是 python寫的 一個(gè)第三方開源工具快骗,用來解析 Redis 快照文件。除了解析 rdb 文件塔次,還提供了統(tǒng)計(jì)單個(gè) key 大小的工具方篮。
1、安裝
git clone https://github.com/sripathikrishnan/redis-rdb-tools
cd redis-rdb-tools sudo && python setup.py install
2励负、使用
從 dump.rdb 快照文件統(tǒng)計(jì), 將所有 > 10kb 的 key 輸出到一個(gè) csv 文件
rdb dump.rdb -c memory --bytes 10240 -f live_redis.csv
2.5.3 使用 memory 命令查看 key 的大信航Α(僅支持 Redis 4.0 以后的版本)
redis-cli -h 127.0.0.1 -p 6379 -a password
MEMORY USAGE keyname1
(integer) 157481
MEMORY USAGE keyname2
(integer) 312583
3 HotKey
3.1 定義
通常以其接收到的Key被請求頻率來判定,例如:
QPS集中在特定的Key:Redis實(shí)例的總QPS(每秒查詢率)為10,000继榆,而其中一個(gè)Key的每秒訪問量達(dá)到了7,000巾表。
帶寬使用率集中在特定的Key:對一個(gè)擁有上千個(gè)成員且總大小為1 MB的HASH Key每秒發(fā)送大量的HGETALL操作請求。
CPU使用時(shí)間占比集中在特定的Key:對一個(gè)擁有數(shù)萬個(gè)成員的Key(ZSET類型)每秒發(fā)送大量的ZRANGE操作請求略吨。
也就是說雖然每個(gè)集群實(shí)例上的數(shù)據(jù)量相差不大攒发,但是某個(gè)實(shí)例上的數(shù)據(jù)是熱點(diǎn)數(shù)據(jù),被訪問得非常頻繁
3.2 產(chǎn)生原因
預(yù)期外的訪問量陡增晋南,如突然出現(xiàn)的爆款商品惠猿、訪問量暴漲的熱點(diǎn)新聞、直播間某主播搞活動帶來的大量刷屏點(diǎn)贊、游戲中某區(qū)域發(fā)生多個(gè)工會之間的戰(zhàn)斗涉及大量玩家等偶妖。
3.3 危害
占用大量的CPU資源姜凄,影響其他請求并導(dǎo)致整體性能降低。
集群架構(gòu)下趾访,產(chǎn)生訪問傾斜态秧,即某個(gè)數(shù)據(jù)分片被大量訪問,而其他數(shù)據(jù)分片處于空閑狀態(tài)扼鞋,可能引起該數(shù)據(jù)分片的連接數(shù)被耗盡申鱼,新的連接建立請求被拒絕等問題。
在搶購或秒殺場景下云头,可能因商品對應(yīng)庫存Key的請求量過大捐友,超出Redis處理能力造成超賣。
熱Key的請求壓力數(shù)量超出Redis的承受能力易造成緩存擊穿溃槐,即大量請求將被直接指向后端的存儲層匣砖,導(dǎo)致存儲訪問量激增甚至宕機(jī),從而影響其他業(yè)務(wù)昏滴。
3.4 優(yōu)化方案
3.4.1 備份熱key
可以把熱點(diǎn)數(shù)據(jù)復(fù)制多份猴鲫,在每一個(gè)數(shù)據(jù)副本的 key 中增加一個(gè)隨機(jī)后綴,讓它和其它副本數(shù)據(jù)不會被映射到同一個(gè) Slot 中谣殊。
這里相當(dāng)于把一份數(shù)據(jù)復(fù)制到其他實(shí)例上拂共,這樣在訪問的時(shí)候也增加隨機(jī)前綴,將對一個(gè)實(shí)例的訪問壓力姻几,均攤到其他實(shí)例上
3.4.2 本地緩存
如果我們可以預(yù)測到hotkey發(fā)生匣缘,比如秒殺這類,那我們就可以在活動上線前提前把緩存數(shù)據(jù) 寫入到本地內(nèi)存鲜棠。
3.4.3 本地緩存+動態(tài)計(jì)算自動發(fā)現(xiàn)熱點(diǎn)緩存
處理突發(fā)的hotkey就只能通過 動態(tài)計(jì)算然后緩存進(jìn)行熱點(diǎn)緩存
3.5 監(jiān)控并預(yù)警
3.5.1 基于流式計(jì)算技術(shù)的緩存熱點(diǎn)?動發(fā)現(xiàn)
框架如下圖肌厨,具體介紹見 Redis(五):常見面試題目詳解
3.5.2 京東開源框架JD-hotkey
JD-hotkey框架如下圖,git地址 https://gitee.com/jd-platform-opensource/hotkey
流程介紹:
客戶端通過引用hotkey的client包豁陆,在啟動的時(shí)候上報(bào)自己的信息給worker柑爸,同時(shí)和worker之間建立長連接。定時(shí)拉取配置中心上面的規(guī)則信息和worker集群信息盒音。
客戶端調(diào)用hotkey的ishot()的方法來首先匹配規(guī)則表鳍,然后統(tǒng)計(jì)是不是熱key。
通過定時(shí)任務(wù)把熱key數(shù)據(jù)上傳到worker節(jié)點(diǎn)祥诽。
worker集群在收取到所有關(guān)于這個(gè)key的數(shù)據(jù)以后(因?yàn)橥ㄟ^hash來決定key 上傳到哪個(gè)worker的譬圣,所以同一個(gè)key只會在同一個(gè)worker節(jié)點(diǎn)上),在和定義的規(guī)則進(jìn)行匹配后判斷是不是熱key雄坪,如果是則推送給客戶端厘熟,完成本地緩存。
1)etcd集群 etcd作為一個(gè)高性能的配置中心,可以以極小的資源占用绳姨,提供高效的監(jiān)聽訂閱服務(wù)登澜。主要用于存放規(guī)則配置,各worker的ip地址飘庄,以及探測出的熱key脑蠕、手工添加的熱key等。
2)client端jar包 就是在服務(wù)中添加的引用jar跪削,引入后谴仙,就可以以便捷的方式去判斷某key是否熱key。同時(shí)碾盐,該jar完成了key上報(bào)晃跺、監(jiān)聽etcd里的rule變化、worker信息變化廓旬、熱key變化哼审,對熱key進(jìn)行本地caffeine緩存等谐腰。
3) worker端集群 worker端是一個(gè)獨(dú)立部署的Java程序孕豹,啟動后會連接etcd,并定期上報(bào)自己的ip信息十气,供client端獲取地址并進(jìn)行長連接励背。之后,主要就是對各個(gè)client發(fā)來的待測key進(jìn)行累加計(jì)算砸西,當(dāng)達(dá)到etcd里設(shè)定的rule閾值后叶眉,將熱key推送到各個(gè)client。
4) dashboard控制臺 控制臺是一個(gè)帶可視化界面的Java程序芹枷,也是連接到etcd衅疙,之后在控制臺設(shè)置各個(gè)APP的key規(guī)則,譬如2秒20次算熱鸳慈。然后當(dāng)worker探測出來熱key后饱溢,會將key發(fā)往etcd,dashboard也會監(jiān)聽熱key信息走芋,進(jìn)行入庫保存記錄绩郎。同時(shí),dashboard也可以手工添加翁逞、刪除熱key肋杖,供各個(gè)client端監(jiān)聽。
下面幾篇文章是作者寫的挖函,對于框架理解很有意義状植。
京東開源熱key探測(JD-hotkey)中間件單機(jī)qps 2萬提升至35萬實(shí)錄
京東毫秒級熱key探測框架設(shè)計(jì)與實(shí)踐,已實(shí)戰(zhàn)于618大促
從性能上來說,明顯的可以看出開源框架JD-hotkey很優(yōu)秀浅萧,只是第一個(gè)方案可以自定義開發(fā)逐沙,開源框架JD-hotkey就只能使用了。
補(bǔ)充
etcd簡介
etcd 這個(gè)名字由兩部分組成: etc 和 d 洼畅,即 UNIX/Linux操作系統(tǒng)的“/etc” 目錄和分布式( distributed )首字母的“d ” 吩案。我們都知道,/etc目錄一般用于存 儲 UNIX/Linux 操作系統(tǒng)的配置信息 因此etc和d合起來就是一個(gè)分布式的/etc 目錄帝簇。etcd 的寓意是為大規(guī)模分布式系統(tǒng)存儲配置信息徘郭。
etcd 的官方定義是一個(gè) Go 語言編寫的分布式、高可用 的用于分布式系統(tǒng)重要數(shù)據(jù)存儲的 一致性鍵值存儲 系統(tǒng)丧肴,常用于提供可靠的分布式鍵值( key-value )存儲残揉、配置共享和服務(wù)發(fā)現(xiàn)等 功能。 etcd 具有容錯能力芋浮,對于n個(gè)節(jié)點(diǎn)的集群抱环,可以在n-1/2 個(gè)節(jié)點(diǎn)宕機(jī)(其他節(jié)點(diǎn)正常) 的情況下仍繼續(xù)工作。
etcd應(yīng)用場景
etcd 的定位是通用的一致性 key/value 存儲纸巷,但也有 服務(wù)發(fā)現(xiàn)和共享配置 的功能 镇草。 因此,典型的 etcd 應(yīng)用場景包括但不限于分布式 數(shù)據(jù)庫瘤旨、服務(wù)注冊與發(fā)現(xiàn) 梯啤、 分布式鎖 、 分布式消息存哲、隊(duì)列 因宇、 分布式系統(tǒng)選主等 。 etcd 的定位是通用的一致性 key/value 存儲祟偷,同時(shí)也面向服務(wù)注冊與發(fā)現(xiàn)的應(yīng)用 場景 察滑。
etcd vs Zookeeper
Zoo Keeper 的主要優(yōu)勢是其具有成熟、健壯以及豐 富 的特性修肠,然而贺辰,它也 有自己的缺點(diǎn),具體如下:
復(fù)雜 氛赐。 ZooKeeper 的部署維護(hù)比較復(fù)雜魂爪;Paxos 強(qiáng)一致性算法也復(fù)雜難懂;使用也比較復(fù)雜艰管,需要安裝客戶端滓侍,官方 只提供了 Java 和 C 兩種語言的接口
Java 編寫,Java 本身就偏向重型應(yīng)用依賴較多牲芋,對資源的占用也比較高
發(fā)展緩慢 ,Apache 基金會龐大的結(jié)構(gòu)和松散的管理導(dǎo)致項(xiàng)目發(fā) 展緩慢
與 ZooKeeper 相比撩笆,ETCD更簡單捺球,安 裝、部署和使用更加容易夕冲,并且 etcd 的某些功能是 ZooKeeper 所沒有的,比如:
etcd 更加穩(wěn)定可靠氮兵,它的唯一目標(biāo)就是把分布式一致性 KV 存儲做到極 致,所以它更注重穩(wěn)定性和擴(kuò)展性
服務(wù)發(fā)現(xiàn)歹鱼,etcd 使用的是節(jié)點(diǎn)租約( Lease )泣栈,并且支持Group (多 key );而 ZooKeeper 使用的是臨時(shí)節(jié)點(diǎn)弥姻,臨時(shí)節(jié)點(diǎn)存在很多問題
etcd 支持穩(wěn)定的 watch 南片,而不是 ZooKeeper 一樣簡單的單次觸發(fā)式watch,很多調(diào)度系統(tǒng)需要得到完整節(jié)點(diǎn)歷史記錄庭敦,etcd 可以存儲數(shù)十萬個(gè)歷史變更
etcd 支持 MVCC (多版本并發(fā)控制)疼进,因?yàn)橛袇f(xié)同系統(tǒng)需要無鎖操作
etcd 支持更大的數(shù)據(jù)規(guī)模 , 支持存儲百萬到千萬級別的 key
etcd 的性能更好 秧廉。 在 一 個(gè)由 3 臺 8 核節(jié)點(diǎn)組成的云 服務(wù)器上伞广, etcd d 版本可以做到每秒數(shù)萬次的寫操作和數(shù)十萬次的讀操作
數(shù)據(jù)持久化。etcd默認(rèn)數(shù)據(jù)一更新就進(jìn)行持久化疼电。
最后嚼锄,etcd作為一個(gè)年輕的項(xiàng)目,正在高速迭代和開發(fā)中澜沟,這既是一個(gè)優(yōu)點(diǎn)灾票,也是一個(gè)缺點(diǎn)峡谊。優(yōu)點(diǎn)在于它的未來具有無限的可能性茫虽,缺點(diǎn)是版本的迭代導(dǎo)致其使用的可靠性無法保證,無法得到大項(xiàng)目長時(shí)間使用的檢驗(yàn)既们。然而濒析,目前CoreOS、Kubernetes和Cloudfoundry等知名項(xiàng)目均在生產(chǎn)環(huán)境中使用了etcd啥纸,所以總的來說号杏,etcd值得你去嘗試。
JD-hotkey 為什么用etcd斯棒,而不是zookeeper
etcd里面具備一個(gè)過期刪除的功能盾致,你可以設(shè)置一個(gè)key幾秒過期,etcd會自動刪除它荣暮,刪除時(shí)還會給所有監(jiān)聽的client回調(diào)庭惜,這個(gè)功能在框架里是在用的,別的配置中心沒有這個(gè)功能穗酥。
etcd的性能和穩(wěn)定性护赊、低負(fù)載等各項(xiàng)指標(biāo)非常優(yōu)異惠遏,完全滿足我們的需求。而zk在很多暴漲流量前和高負(fù)載下骏啰,并不是那么穩(wěn)定节吮,性能也差的遠(yuǎn)。