淺析Redis的BigKey(阿里巴巴技術(shù)協(xié)會(huì)ATA同步發(fā)送)

在完成事件接入的需求時(shí)蔑担,我們需要記錄上一個(gè)批次拉取的事件扶关,并與當(dāng)前拉取到的事件做出比對(duì)缔逛,從而進(jìn)行差分。我們目前的做法是使用redis來進(jìn)行緩存:將上一個(gè)批次拉取到的事件緩存到一個(gè)list中冰悠。但是當(dāng)事件數(shù)量過多時(shí)堡妒,value的大小會(huì)超過1M的限制,直接拋出異常溉卓。這其實(shí)是Tair出于性能的考慮而做出的限制皮迟,本文將談?wù)勎覀€(gè)人對(duì)于bigKey的理解。

1.什么是BigKey桑寨?

顧名思義伏尼,bigKey指一個(gè)key對(duì)應(yīng)的value占據(jù)的內(nèi)存空間相對(duì)比較大,bigKey通常會(huì)有兩種表現(xiàn)形式:

  1. 字符串類型的:通常表現(xiàn)為value大于10k的String類型key尉尾。
  2. 非字符串類型/集合類型:通常表現(xiàn)為存儲(chǔ)了過多元素的List爆阶、Hash、Set沙咏、ZSet類型key辨图。

bigKey一旦產(chǎn)生,將會(huì)對(duì)tair的性能以及穩(wěn)定性造成較大的影響肢藐,下面我將詳細(xì)介紹一下bigKey的危害故河。

2.BigKey有什么危害?

bigKey給tair帶來的危害是多方面的窖壕,性能下降只是其中的一方面忧勿,極端情況下杉女,bigKey甚至?xí)?dǎo)致緩存服務(wù)崩潰瞻讽。下面我將從幾個(gè)角度進(jìn)行分析。

2.1 性能影響

2.1.1 線程阻塞

由于redis采用的是單線程模型熏挎,對(duì)于key的增刪改查都是在主線程中完成速勇。此時(shí),對(duì)于bigKey的操作將會(huì)阻塞主線程坎拐,成為一個(gè)明顯的性能瓶頸烦磁,以對(duì)bigKey的刪除耗時(shí)為例:
image.png

我們可以看到:

  1. 當(dāng)集合類型key中的元素?cái)?shù)量從10萬增加到100萬時(shí)养匈,其刪除的耗時(shí)也成倍的增長。
  2. 當(dāng)集合類型key中單個(gè)元素的大小增加時(shí)都伪,其刪除的耗時(shí)也相應(yīng)的增長呕乎。

另外,在Redis執(zhí)行異步重寫操作時(shí)(bgrewriteaof)陨晶,主線程會(huì)fork出一個(gè)子進(jìn)程來執(zhí)行重寫命令猬仁,這個(gè)子進(jìn)程會(huì)與主線程共享內(nèi)存依痊。當(dāng)主線程收到了新增或者修改一個(gè)key的命令伐坏,主線程會(huì)申請(qǐng)一塊額外的內(nèi)存空間來保存數(shù)據(jù)滤馍。但如果這個(gè)key是一個(gè)bigKey時(shí)采幌,主線程會(huì)去申請(qǐng)一塊更大空間书斜,同樣會(huì)阻塞主線程(與JVM分配內(nèi)存一樣沪停,涉及鎖和同步)唠倦。如果申請(qǐng)不到足夠的空間厂镇,會(huì)導(dǎo)致Swap甚至?xí)蠴OM的風(fēng)險(xiǎn)铃芦,這同樣會(huì)降低Redis的性能和穩(wěn)定性雅镊。

2.1.2 網(wǎng)絡(luò)阻塞

Tair中一個(gè)key最大為1M,我們就以1M舉例刃滓,當(dāng)訪問這個(gè)key的QPS為1000時(shí)漓穿,每秒將會(huì)有1GB左右的流量,對(duì)于帶寬來說將是一個(gè)較大壓力注盈。如果這個(gè)bigKey是一個(gè)熱點(diǎn)key時(shí)晃危,后果將不堪設(shè)想。

2.1.3 數(shù)據(jù)遷移阻塞

如果主從同步的 client-output-buffer-limit 設(shè)置過小老客,并且 master 存在大量bigKey(數(shù)據(jù)量很大)僚饭,主從全量同步時(shí)可能會(huì)導(dǎo)致 buffer 溢出,溢出后主從全量同步就會(huì)失敗胧砰。如果主從集群配置了哨兵鳍鸵,那么哨兵會(huì)讓 slave 繼續(xù)向 master 發(fā)起全量同步請(qǐng)求,然后 buffer 又溢出同步失敗尉间,如此反復(fù)偿乖,會(huì)形成復(fù)制風(fēng)暴,這會(huì)浪費(fèi) master 大量的 CPU哲嘲、內(nèi)存贪薪、帶寬資源,也會(huì)讓 master 產(chǎn)生阻塞的風(fēng)險(xiǎn)眠副。 另外画切,當(dāng)我們使用Redis Cluster時(shí),由于Redis Cluster采用了同步遷移的方式囱怕,bigKey同樣會(huì)阻塞主線程霍弹。這里提一下Codis毫别,Codis在遷移bigKey時(shí),使用了異步遷移 + 指令拆分的方式典格,對(duì)于bigKey (集合類型) 中每個(gè)元素岛宦,用一條指令進(jìn)行遷移,而不是把整個(gè) bigKey 進(jìn)行序列化后再整體傳輸耍缴。這種化整為零的方式恋博,就避免了 bigKey 遷移時(shí),因?yàn)橐蛄谢罅繑?shù)據(jù)而阻塞的問題私恬。

2.2 穩(wěn)定性影響

眾所周知债沮,Redis 是典型的 client-server 架構(gòu),所有的操作命令都需要通過客戶端發(fā)送給服務(wù)器端本鸣。為了避免客戶端和服務(wù)器端的請(qǐng)求發(fā)送和處理速度不匹配疫衩,服務(wù)器為每個(gè)客戶端都分配了輸入緩沖區(qū)和輸出緩沖區(qū)(默認(rèn)大小為1GB),用于緩存客戶端發(fā)送的命令和服務(wù)端返回的數(shù)據(jù)荣德。
image.png

當(dāng)我們寫入或者讀取大量bigKey的時(shí)候闷煤,很有可能導(dǎo)致輸入/輸出緩沖區(qū)溢出。如果客戶端占用的內(nèi)存總量超過了服務(wù)器設(shè)置的maxmemory時(shí)(默認(rèn)4GB)涮瞻,將會(huì)直接觸發(fā)服務(wù)器的內(nèi)存淘汰策略鲤拿,如果有數(shù)據(jù)被淘汰,再要獲取這些數(shù)據(jù)就需要到后端回源署咽,間接降低了緩存系統(tǒng)的性能近顷。同時(shí),淘汰的如果是bigKey也同樣會(huì)阻塞主線程宁否。另外窒升,在極端情況下,多個(gè)客戶端占用了過多的內(nèi)存將導(dǎo)致OOM慕匠,進(jìn)而使得整個(gè)redis進(jìn)程崩潰饱须。

2.3 數(shù)據(jù)傾斜

使用切片集群的時(shí)候,我們通常會(huì)將不同的key存放在不同的實(shí)例上台谊,如果存在bigKey的話蓉媳,會(huì)導(dǎo)致相應(yīng)實(shí)例的數(shù)據(jù)量增大,內(nèi)存壓力也相應(yīng)增大锅铅。

3.怎樣發(fā)現(xiàn)BigKey酪呻?

常用的做法是通過./redis-cli --bigkeys命令對(duì)整個(gè)redis中的鍵值對(duì)進(jìn)行統(tǒng)計(jì),輸出每種數(shù)據(jù)類型中最大的 bigkey 的信息狠角。一般會(huì)配合-i參數(shù)一起使用,控制掃描間隔号杠,避免長時(shí)間掃描降低 Redis 實(shí)例的性能。另外該命令不要在業(yè)務(wù)高峰期使用丰歌。

./redis-cli  --bigkeys
-------- summary -------
Sampled 32 keys in the keyspace!
Total key length in bytes is 184 (avg len 5.75)

//統(tǒng)計(jì)每種數(shù)據(jù)類型中元素個(gè)數(shù)最多的bigkey
Biggest   list found 'product1' has 8 items
Biggest   hash found 'dtemp' has 5 fields
Biggest string found 'page2' has 28 bytes
Biggest stream found 'mqstream' has 4 entries
Biggest    set found 'userid' has 5 members
Biggest   zset found 'device:temperature' has 6 members

//統(tǒng)計(jì)每種數(shù)據(jù)類型的總鍵值個(gè)數(shù)姨蟋,占所有鍵值個(gè)數(shù)的比例,以及平均大小
4 lists with 15 items (12.50% of keys, avg size 3.75)
5 hashs with 14 fields (15.62% of keys, avg size 2.80)
10 strings with 68 bytes (31.25% of keys, avg size 6.80)
1 streams with 4 entries (03.12% of keys, avg size 4.00)
7 sets with 19 members (21.88% of keys, avg size 2.71)
5 zsets with 17 members (15.62% of keys, avg size 3.40)

或者我們可以通過debug object key 命令去查看serializedlength屬性立帖,serializedlength表示key對(duì)應(yīng)的value序列化后的字節(jié)數(shù)眼溶,通過觀察serializedlength的大小可以輔助排查bigKey。使用scan + debug object key命令晓勇,我們可以計(jì)算其中每個(gè)key的serializedlength堂飞,進(jìn)而發(fā)現(xiàn)其中的bigKey,并做好相應(yīng)的監(jiān)控和處理绑咱。不過對(duì)于集合類型的bigKey绰筛,debug object key 命令的執(zhí)行效率不高,存在阻塞redis的風(fēng)險(xiǎn)描融。

4.怎樣避免和處理BigKey铝噩?

對(duì)于字符串類型的key,我們通常要在業(yè)務(wù)層面將value的大小控制在10KB左右窿克,如果value確實(shí)很大骏庸,可以考慮采用序列化算法和壓縮算法來處理,推薦常用的幾種序列化算法:Protostuff年叮、Kryo或者Fst具被。以及常用的壓縮算法:zstd、lz4或者谷歌的snappy(需要根據(jù)吞吐量和壓縮比自行取舍)只损。下面附上各種壓縮算法的相關(guān)性能:
image.png

(來源:Facebook Zstandard 官網(wǎng))
對(duì)于集合類型的key一姿,我們通常要通過控制集合內(nèi)元素?cái)?shù)量來避免bigKey,通常的做法是將一個(gè)大的集合類型的key拆分成若干小集合類型的key來達(dá)到目的跃惫。值得一提的是啸蜜,List、Hash辈挂、Set 和ZSet來說衬横,在集合元素個(gè)數(shù)和元素大小小于一定的閾值時(shí),會(huì)使用內(nèi)存緊湊型的底層數(shù)據(jù)結(jié)構(gòu)進(jìn)行保存终蒂,從而節(jié)省內(nèi)存蜂林,規(guī)則如下:

  1. List:當(dāng)List對(duì)象保存的所有字符串元素長度都小于list-max-ziplist-value(默認(rèn)64字節(jié)),且List對(duì)象保存的元素?cái)?shù)量小于list-max-ziplist-entries(默認(rèn)512)時(shí),List對(duì)象將采用ziplist編碼以節(jié)省內(nèi)存拇泣。
  2. Hash:當(dāng)Hash對(duì)象保存的鍵值對(duì)的key和value的字符串長度都小于hash-max-ziplist-value(默認(rèn)64字節(jié))噪叙,且Hash對(duì)象保持的鍵值對(duì)數(shù)量小于hash-max-ziplist-entries(默認(rèn)512)時(shí),Hash對(duì)象將采用ziplist編碼以節(jié)省內(nèi)存霉翔。
  3. Set:當(dāng)Set對(duì)象保存的所有元素都是整數(shù)值睁蕾,且Set對(duì)象保存的元素?cái)?shù)量不超過set-max-intset-entries(默認(rèn)512)時(shí),Set對(duì)象將采用intset編碼以節(jié)省內(nèi)存。
  4. ZSet:當(dāng)ZSet對(duì)象保存的元素?cái)?shù)量小于zset-max-ziplist-entries(默認(rèn)128)子眶,且ZSet對(duì)象保存的所有元素的長度小于zset-max-ziplist-value(默認(rèn)64)時(shí)瀑凝,ZSet對(duì)象將采用ziplist編碼以節(jié)省內(nèi)存。

另外臭杰,在讀取bigKey的時(shí)候粤咪,我們盡量不要一次性將全部數(shù)據(jù)讀取出來,而是采用分批的方式進(jìn)行讀取:利用scan命令進(jìn)行漸進(jìn)式遍歷渴杆,將大量數(shù)據(jù)分批多次讀取出來寥枝,減小redis的壓力,避免阻塞的風(fēng)險(xiǎn)磁奖。
同樣的囊拜,在刪除bigKey的時(shí)候我們也可以使用scan命令來進(jìn)行批量刪除。如果你是用的redis是4.0之后的版本比搭,則可以利用unlink命令配合lazy free配置(需要手動(dòng)開啟)來進(jìn)行異步刪除冠跷,避免主線程阻塞。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末敢辩,一起剝皮案震驚了整個(gè)濱河市蔽莱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌戚长,老刑警劉巖盗冷,帶你破解...
    沈念sama閱讀 211,348評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異同廉,居然都是意外死亡仪糖,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門迫肖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锅劝,“玉大人,你說我怎么就攤上這事蟆湖」示簦” “怎么了?”我有些...
    開封第一講書人閱讀 156,936評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵隅津,是天一觀的道長诬垂。 經(jīng)常有香客問我,道長伦仍,這世上最難降的妖魔是什么结窘? 我笑而不...
    開封第一講書人閱讀 56,427評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮充蓝,結(jié)果婚禮上隧枫,老公的妹妹穿的比我還像新娘喉磁。我一直安慰自己,他們只是感情好官脓,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評(píng)論 6 385
  • 文/花漫 我一把揭開白布协怒。 她就那樣靜靜地躺著,像睡著了一般确买。 火紅的嫁衣襯著肌膚如雪斤讥。 梳的紋絲不亂的頭發(fā)上纱皆,一...
    開封第一講書人閱讀 49,785評(píng)論 1 290
  • 那天湾趾,我揣著相機(jī)與錄音,去河邊找鬼派草。 笑死搀缠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的近迁。 我是一名探鬼主播艺普,決...
    沈念sama閱讀 38,931評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鉴竭!你這毒婦竟也來了歧譬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,696評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤搏存,失蹤者是張志新(化名)和其女友劉穎瑰步,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體璧眠,經(jīng)...
    沈念sama閱讀 44,141評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缩焦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了责静。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袁滥。...
    茶點(diǎn)故事閱讀 38,625評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖灾螃,靈堂內(nèi)的尸體忽然破棺而出题翻,到底是詐尸還是另有隱情,我是刑警寧澤腰鬼,帶...
    沈念sama閱讀 34,291評(píng)論 4 329
  • 正文 年R本政府宣布嵌赠,位于F島的核電站,受9級(jí)特大地震影響垃喊,放射性物質(zhì)發(fā)生泄漏猾普。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評(píng)論 3 312
  • 文/蒙蒙 一本谜、第九天 我趴在偏房一處隱蔽的房頂上張望初家。 院中可真熱鬧,春花似錦、人聲如沸溜在。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽掖肋。三九已至仆葡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間志笼,已是汗流浹背沿盅。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留纫溃,地道東北人腰涧。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像紊浩,于是被迫代替她去往敵國和親窖铡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容