對于分布式數(shù)據(jù)庫來說玄渗,熱點和事務沖突是兩個需要避免的場景粱檀,在很多客戶測試的案例中,經常出現(xiàn)熱點引起的性能未達預期的情況败玉。本文借近期遇到的幾個客戶場景,對熱點問題在 TiDB 中的表現(xiàn)形式和影響占业,以及如何應對做一個記錄绒怨。
何為熱點
熱點可以理解為熱點數(shù)據(jù),或者說熱點 region谦疾,TiDB 自帶的 grafana 監(jiān)控指標中也有 hot region write / read 的 metrics南蹂。但是我的理解熱點問題更準確的表現(xiàn)形式,其實是 某(幾)個tikv 節(jié)點的 corprocessor / scheduler (負責讀/寫模塊的線程)消耗資源過高念恍,而剩下的 kv 節(jié)點資源白白閑置六剥。對于分布式系統(tǒng)來說晚顷,優(yōu)點突出但是可能存在木桶效應,某一個組件/服務器資源瓶頸疗疟,會影響到整個集群的表現(xiàn)该默。
判斷熱點現(xiàn)象也很簡單,查看 tikv 監(jiān)控頁面的 thread CPU 監(jiān)控項策彤,如果發(fā)現(xiàn) corprocessor 或 scheduler 中各 kv 實例的 CPU 消耗十分不均勻栓袖,那大概率就是碰到熱點現(xiàn)象了。
產生熱點的原因
產生熱點現(xiàn)象的原因有多種店诗,大致總結可分為以下:
1裹刮、部分小表例如配置表的頻繁訪問引起熱點
2、MySQL 中自增主鍵的高并發(fā)寫入
3庞瘸、非自增捧弃,但時間相關的順序插入
4、無主鍵擦囊,或主鍵為非 int 類型
5违霞、時間相關字段的索引
6、業(yè)務邏輯/數(shù)據(jù)分布產生的熱點讀寫
7瞬场、執(zhí)行計劃不合理引起的非必要全表/錯誤的索引掃描
如何規(guī)避
熱點的解決思路有兩種买鸽,一是加快單次處理的速度,二是將頻繁請求的數(shù)據(jù)分散到不同的 region贯被,然后通過 pd 調度或手工的方式癞谒,將 region 的 leader 調度到多個kv 實例中。
針對上面的情況刃榨,逐一分析弹砚。
第一種小表頻繁訪問的場景,因為數(shù)據(jù)量少枢希,而 TiKV 默認的 region 大小為64M桌吃,基本上這些數(shù)據(jù)都會分在一個 region,如果業(yè)務高并發(fā)訪問苞轿,勢必會引起熱點茅诱。這種主要是通過業(yè)務手段來規(guī)避,比較常見的做法是將配置表數(shù)據(jù)放到緩存中搬卒。從數(shù)據(jù)庫角度優(yōu)化瑟俭,可以通過 pd-ctl 找到熱點 region吱七,確認對應的配置表后玉掸,可以手動將熱點 region split 為多個,后續(xù) pd 就可以通過調度算法將這幾個不同的 region leader 調度到不同的 kv 節(jié)點军浆,緩解熱點情況。
第二~四種場景也是比較常見的微饥,一般 MySQL 中都會建議采用 auto_increment 字段作為主鍵逗扒,或者有些業(yè)務例如訂單系統(tǒng)雖然沒有用自增主鍵,但是基于時間戳來生成一個業(yè)務 ID 作為主鍵欠橘,這種對于TiDB 來說跟自增的場景也比較類似矩肩。另外還有些時候,客戶會選擇非 int 類型的字段作為主鍵肃续,例如手機號碼存為 varchar 等黍檩。對于這種非 int 類型的主鍵,TiDB 內部會做一個轉換始锚,添加一個自增的 bigint 類型作為主鍵建炫。所以這幾個場景如果出現(xiàn)高并發(fā)的大量寫入,目前2.0/2.1版本中疼蛾,基本上單表 TPS 超過1W 就有可能會產生明顯的熱點效應了。如果想解決可以對表結構做一些改造艺配,將原主鍵設為 Unique Key察郁,這樣 TiDB 內部會添加一個自增的偽列 _tidb_rowid。我們可以通過 alter table t shard_row_id_bits = 4; 的語句來將數(shù)據(jù)打散转唉。此語法只對沒有顯示指定 PK 或 PK 為非整數(shù)類型時才有效皮钠,打散后插入效率可以大大提升,但是會帶來一定的副作用就是進行范圍 scan 的時候赠法,打散前可能只需要掃一個 region麦轰,打散后可能需要掃多個 region。
第五種時間字段的索引砖织,在目前2.0版本中款侵,并沒有太好的解決辦法。未來版本中即將開放的 partition table 這個新的特性侧纯,對于這種場景會有一定的緩解新锈。但是在范圍分區(qū)表中,就不能以時間作為分區(qū)鍵了眶熬,可能需要找另外一個字段作為分區(qū)鍵妹笆,這樣才能夠將基于時間的順序寫入切分為多張表來操作以緩解熱點情況。
但是這可能會有兩個問題娜氏,一是這樣就不能利用到時間范圍分區(qū)的最大便利之一的快速歸檔功能拳缠,二是如果基于時間的范圍查找,需要將所有分表都通過索引 scan 一遍再 union 之后返回結果贸弥。
其實可以考慮類似 Oracle 的組合分區(qū)功能窟坐,先按照時間范圍 partition,在每個 partition 里再 hash partition 一下,這樣基于時間的范圍查找仍然能夠定位到大的分區(qū)狸涌,大分區(qū)下面的所有 hash 子分區(qū)必須是要全部 scan 了切省。
第六種需要結合具體的業(yè)務場景來分析,例如某些交易系統(tǒng)中對公賬戶可能會成為熱點賬戶帕胆,這時在業(yè)務側進行拆分朝捆,將一個對公賬戶拆分為10個賬戶。業(yè)務訪問熱點賬戶時懒豹,可以隨機選其中一個賬戶進行操作芙盘,這樣可以有效避免熱點情況的產生,但是統(tǒng)計的時候需要將所有賬戶進行歸并脸秽。另外在對熱點數(shù)據(jù)進行操作的時候儒老,可以考慮在業(yè)務層進行排序/合并,降低對熱點數(shù)據(jù)的訪問頻率记餐。
對于第七種場景驮樊,就是上面所提到的要通過提升單次請求的效率來緩解熱點問題,主要還是通過優(yōu)化慢 SQL 的手段片酝。
熱點 Case 記錄
case1:某平臺業(yè)務壓測寫入瓶頸
該用戶在 TiDB 上進行業(yè)務 PoC 測試囚衔,這是目前線上寫入量最大的業(yè)務單元,峰值 TPS 接近4W雕沿,由于還未采用分庫分表练湿,單個 MySQL 實例幾乎無法滿足這么大的寫入量,所以目前是通過 Hbase 的方式來做审轮。
在測試 TiDB 的過程中肥哎,發(fā)現(xiàn)在3個 tikv 實例的集群中,寫入量能達到4W疾渣,由于咱們是支持寫擴展的篡诽,加到6個 tikv 實例,但是發(fā)現(xiàn)寫入量還是只有4W榴捡。在另外一套12個 kv 節(jié)點的集群上進行測試霞捡,寫入也只有4W,無法提升薄疚。雖然滿足目前線上峰值的需求碧信,但是寫入的水平擴展能力并沒有得到驗證。
通過觀察 grafana 監(jiān)控街夭,發(fā)現(xiàn)負責寫入的 scheduler 線程有明顯的熱點情況砰碴。確認客戶的表結構,是一個業(yè)務無關的自增 ID 作為主鍵板丽, 通過上面shard 的方式打散表后測試寫入呈枉,在6個 tikv 實例的時候寫入能夠達到7W趁尼,12個 tikv 實例的時候寫入能夠穩(wěn)定在11W+,不僅寫入能力大大超出客戶預期猖辫,水平擴展能力也得到驗證酥泞。
case2:某平臺業(yè)務壓測寫入瓶頸
用戶在 TiDB 上測試單張業(yè)務表(超過20億數(shù)據(jù))的寫入性能,由于表結構較為復雜啃憎,平均長度達到了2kb芝囤,在使用自增 ID 的情況下,寫入瓶頸在2.5W QPS辛萍。
使用上述方式修改表結構為 shard 模式后悯姊,發(fā)現(xiàn)仍然存在熱點現(xiàn)象,后確認表結構存在兩個時間字段的索引贩毕,上面提到索引也可能會造成熱點悯许。先去掉時間索引進行測試,發(fā)現(xiàn) TPS 還是上不去辉阶,熱點現(xiàn)象仍然存在先壕。
跟用戶確認具體的壓測邏輯,業(yè)務代碼自己維護了一張類似計數(shù)器的配置表谆甜,多線程并發(fā)寫入數(shù)據(jù)的時候垃僚,每個線程每次取步長1000,而壓測共采用了40個客戶端模擬店印,并發(fā)量達到了2W,這樣熱點情況沒有出現(xiàn)在業(yè)務表倒慧,反而出現(xiàn)在了負責計數(shù)器功能的配置表上按摘。建議客戶調整步長后,經過多次測試纫谅,在步長為500W 時炫贤,能夠有效避免熱點效應,QPS 能夠達到10W付秕。
這個 case 后面還發(fā)生了一些有意思的現(xiàn)象兰珍。
調整表結構為 shard,步長為500W后询吴,雖然寫入 QPS 能夠達到10W掠河,但是在穩(wěn)定性測試時,發(fā)生過幾分鐘之后 QPS 會降到3W猛计,熱點現(xiàn)象又出現(xiàn)了唠摹。
通過分析,每次熱點的現(xiàn)象都出現(xiàn)在固定的幾個 kv 節(jié)點奉瘤。在高強度的寫入過程中region 需要不斷的分裂并將 leader 調度到其他節(jié)點以平衡各 kv 節(jié)點的資源消耗勾拉。但是通過分析日志發(fā)現(xiàn),每次調度 leader 的時候都會失敗,失敗的原因是其它副本的數(shù)據(jù)寫入進度遠低于該副本藕赞,于是無法調度成功成肘。最后我們發(fā)現(xiàn)這幾個 kv 節(jié)點的 SSD 磁盤寫入性能要明顯優(yōu)于其它節(jié)點。通過調整集群拓撲斧蜕,將寫入性能差距太大的幾臺服務器調整為 tidb-server双霍,熱點情況消失。研發(fā)也提供了兩個途徑來應對這種場景惩激,一是加快分裂的效率店煞,二是優(yōu)化切換 leader 時校驗數(shù)據(jù) gap 的策略。
測完寫后风钻,繼續(xù)壓測讀請求顷蟀,很不幸又遇到熱點了。壓測邏輯是在 ID 最大值和最小值之間隨機取數(shù)發(fā)起讀請求骡技,按照常理推斷鸣个,這種壓測方式是不應該產生熱點的。通過分析推斷布朦,造測試數(shù)據(jù)的時候可能出現(xiàn)了數(shù)據(jù)傾斜囤萤,通過 select count where id > and id < 的方式很容易得到驗證。通過過濾篩選是趴,發(fā)現(xiàn)在10億至20億之間的數(shù)據(jù)較多涛舍,修改測試代碼,隨機 ID 選在這個范圍之內進行測試唆途,發(fā)現(xiàn)熱點情況消失富雅,QPS 達到35W。
case3:某券商讀業(yè)務響應慢
查看 grafana 監(jiān)控肛搬,一共3個 tikv 實例没佑,其中一個 kv 的 corprocessor 消耗 CPU 1000%+,另外兩臺 CPU 確一直閑置温赔,又是一個明顯的熱點情況蛤奢。
通過分析業(yè)務 SQL,業(yè)務表有一個基于 A+B+C 的聯(lián)合主鍵陶贼,同時也有一個 A 的單列索引啤贩,SQL 中 where 條件是基于 A = ? and B = ? and C = ?,2.0.4版本的優(yōu)化器可能存在一些缺陷拜秧,錯誤的選擇了基于 A 的單列索引瓜晤。
通過分析表發(fā)現(xiàn),A 的數(shù)據(jù)分布傾斜十分嚴重腹纳,某些條件下需要掃描5W+ 數(shù)據(jù)痢掠,更合理的執(zhí)行計劃應該是選擇A驱犹、B、C 的聯(lián)合主鍵足画。通過 hint 方式臨時規(guī)避這個問題雄驹,同時在2.0.6版本已經解決這個優(yōu)化器的 BUG,建議客戶升級到新版本后問題解決淹辞。