做者:張博康git
在 TiDB 的架構中,全部的數據按照 range 劃分紅一個個 Region 分佈在多個 TiKV 實例上。隨着數據的寫入,一個集羣中會產生上百萬,甚至千萬個 Region。而量變引發質變,單 TiKV 實例上過多的 Region 無疑會帶來比較大的負擔,進而影響整個集羣的性能表現。github
本文將介紹 TiKV 核心模塊 Raftstore 的處理流程以使你們更好得理解海量 Region 致使性能問題的根源,以及針對這種狀況的一些優化手段。bash
你們都知道在一個 TiKV 實例上會有多個 Region,這些 Region 消息處理就是經過 Raftstore 這個模塊來驅動的,包括 Region 上讀寫請求的處理,Raft log 的持久化以及複製,還有 Raft 的心跳處理等等。爲何 Region 數多了就會影響到整個集羣的性能呢?爲了解釋這個問題,咱們先來看看 TiKV 的核心模塊 Raftstore 是怎樣工做的。網絡
注:該示意圖僅僅表意,不表明代碼層面的實際結構。多線程
上圖是 Raftstore 處理流程的示意圖,能夠看到,從 TiDB 發來的請求會經過 gRPC 和 storage 模塊變成最終的 KV 讀寫消息發往相應的 Region,而這些消息並不會當即處理而是暫存下來。而在 Raftstore 中會輪詢檢查每一個 Region 是否有須要處理的消息。若是有消息,那麼 Raftstore 會驅動 Raft 狀態機去處理這些消息,並根據這些消息所產生的狀態變動去完成一些後續動做。好比,在有寫請求時,Raft 狀態機須要將日誌落盤而且將日誌發送給其餘副本;在達到心跳間隔時,Raft 狀態機須要將心跳信息發送給其餘副本。架構
從上面咱們能夠看到,這麼多 Region 的消息是一個接一個地處理。那麼在 Region 不少的狀況下,Raftstore 會須要花費一些時間處理大量 Region 的心跳,勢必會引入一些延遲,致使一些讀寫請求得不到特別及時的處理。若是在讀寫壓力大的狀況下,很容易使得 Raftstore 線程 CPU 達到瓶頸,而延遲會被進一步放大,進而影響性能表現。併發
通常來講,在有負載的狀況下,若是 TiKV 的 Raftstore CPU 使用率達到了 85%+(指的是單線程的狀況,多線程等比例放大),咱們就認爲 Raftstore 已經達到了比較繁忙的狀態成爲了瓶頸(因爲 Raftstore 線程中有 IO 操做,因此 CPU 使用率不可能達到 100%),同時 propose wait duration 可能會達到百毫秒級別。app
相關 metrics 可在 TiKV grafana 面板下查看:性能
Thread-CPU 下的 Raft store CPU
優化
參考值:最好低於 raftstore.store-pool-size * 85%
(v2.1 版本中無此配置項,可認爲 raftstore.store-pool-size = 1
)。
Raft Propose 下的 Propose wait duration
Propose wait duration
是發送請求給 Raftstore、到 Raftstore 真正處理請求之間的延遲。若是該延遲比較長,說明 Raftstore 比較繁忙或者 append log 比較耗時致使 Raftstore 不能及時處理請求。
參考值:最好低於 50-100ms。
既然咱們已經知道了性能問題的根源,那麼就能夠從兩方面入手:減小單個 TiKV 實例的 Region 數;減小單個 Region 的消息數。根據不一樣版本,具體能夠參考如下優化方法:
在 v2.1 版本中 Raftstore 只能是單線程,所以通常在 Region 數超過 10 萬時就會逐漸成爲瓶頸。
1. 增長 TiKV 實例
若是 IO 資源和 CPU 資源都還有比較多的盈餘的話,能夠在單個機器上部署多個 TiKV 實例,以減小單個 TiKV 實例上的 Region 個數,或者擴容集羣的 TiKV 機器數。
2. 開啓 Region Merge
另一種能夠減小 Region 個數的辦法是開啓 Region Merge。與 Region Split 相反,Region Merge 是經過調度把相鄰的小 Region 合併的過程。在集羣有刪除數據或者進行過 Drop Table/Truncate Table 後,能夠將那些小 Region 甚至空 Region 進行合併以減小資源的消耗。
簡單來講,經過 pd-ctl 設置相關參數便可開啓 Region Merge
>> pd-ctl config set max-merge-region-size 20
>> pd-ctl config set max-merge-region-keys 200000
>> pd-ctl config set merge-schedule-limit 8
複製代碼
關於更多詳情請參考這兩個文檔 如何配置 Region Merge 和 PD 配置文件描述,在此再也不展開。
同時,默認配置的 Region Merge 默認參數設置相對保守,能夠根據需求參考 《TiDB 最佳實踐系列(二)PD 調度策略》 中說起的具體方法加快 Region Merge 速度。
3. 調整 raft-base-tick-interval
除了減少 Region 個數,咱們還能夠經過儘可能減小 Region 單位時間內的消息數量以減少 Raftstore 壓力。好比,在 TiKV 配置中適當增大 raft-base-tick-interval
:
[raftstore]
raft-base-tick-interval = 「2s」
複製代碼
raft-base-tick-interval
是 Raftstore 驅動每一個 Region 的 Raft 狀態機的基本時間單位,也就是每隔這麼久就須要向 Raft 狀態機發送一個 tick 消息。顯然增大這個時間間隔,能夠有效減小 Raftstore 消息數量。
須要注意的是,這個 tick 也決定了 election timeout 和 heartbeat 的間隔。
raft-election-timeout = raft-base-tick-interval * raft-election-timeout-ticks
raft-heartbeat-interval = raft-base-tick-interval * raft-heartbeat-ticks
複製代碼
follower 在 raft-election-timeout
間隔內未收到來自 leader 的心跳會認爲 leader 出現故障而發起新的選舉,而 raft-heartbeat-interval
是 leader 向 follower 發送心跳的間隔,所以增大 raft-base-tick-interval
能夠減小單位時間內 Raft 發送的網絡消息,但也會讓 Raft 檢測到 leader 故障的時間更長。
除了以上說起的優化方法外(注:Region Merge 在 v3.0 版本中默認開啓),v3.0 版本中還能夠進行如下優化:
1. 提升 Raftstore 併發數
在 v3.0 版本中 Raftstore 已經擴展爲多線程,極大下降了 Raftstore 線程成爲瓶頸的可能性。
默認 TiKV 配置 raftstore.store-pool-size
爲 2
,若是在 Raftstore 出現瓶頸的時候能夠根據實際狀況適當提升,但不建議設置過大以防引入沒必要要的線程切換開銷。
2. 開啓 Hibernate Region
在實際狀況下,讀寫請求並不會均勻的打在每一個 Region 上,而是主要集中在少數的 Region 上,那麼對於暫時空閒的 Region 咱們是否是能夠儘可能減小它們的消息數量。這也就是 Hibernate Region 的主要思想,在無必要的時候不進行 raft-base-tick
,也就是不去驅動那些空閒 Region 的 Raft 狀態機,那麼就不會觸發這些 Region 的 Raft 心跳信息的產生,極大得減少了 Raftstore 的工做負擔。
截止發稿時 Hibernate Region 仍是一個實驗 feature,在 master 上已經默認開啓。若有須要,可酌情開啓,相關配置說明請參考 配置 Hibernate Region。
PD 須要將 Region Meta 信息持久化在 etcd 以保證 PD Leader 節點切換後能快速繼續提供 Region 路由服務。隨着 Region 數量的增加,Etcd 的性能問題會使得 PD 在切換 Leader 時從 etcd 獲取這些信息時比較慢,在百萬 Region 量級時可能達到十幾秒甚至幾十秒。
所以在 v3.0 版本中 PD 將 Region Meta 信息存在本地的 LevelDB 中,經過另外的機制同步 PD 節點間的信息。
在 v3.0 版本中 PD 已經默認開啓配置 use-region-storage
,而 v2.1 版本如碰到相似問題建議升級到 v3.0。
在 TiKV 中是由 pd-worker 這個模塊來將 Region Meta 信息按期上報給 PD,在 TiKV 重啓或者 Region Leader 切換時須要經過統計信息從新計算 Region 的 approximate size/keys
。那麼在 Region 數量比較多的狀況下,pd-worker 單線程可能成爲瓶頸,形成任務的堆積不能及時處理,所以 PD 不能及時獲取某些 Region Meta 信息以至路由信息更新不及時。該問題不會影響實際的讀寫,但可能致使 PD 調度的不許確以及 TiDB 更新 region cache 時須要多幾回 round-trip。
能夠在 TiKV grafana 面板中查看 Task 下的 Worker pending tasks 來肯定 pd-worker 是否有任務堆積,正常來講 pending tasks 應該維持在一個比較低的值。
咱們在 master 上已經對 pd-worker 進行了效率優化,預計會在 v2.1.19 和 v3.0.5 中帶上相關優化,如碰到相似問題建議升級。
在大規模集羣中,TiKV 實例數的增長會讓 Prometheus 的查詢時的計算壓力較大致使 Grafana 查看 metrics 時較慢,在 v3.0 版本中經過設置了一些 metrics 的預計算有所緩解。
本文介紹了百萬級 Region 的集羣規模下的常見問題以及相應的處理方法。整體來說,在 v3.0 版本中咱們作了比較多的優化,海量 Region 致使的性能問題上已經有了明顯的改善。但願本文在問題根源的解釋上能幫助讀者更好的理解相關參數調整背後的邏輯,並能觸類旁通地應用在相似問題的解決上。最後,「量變引發質變」,你們的參與才能讓咱們的產品更進一步,期待大家的反饋和建議(zhangbokang@pingcap.com)。
更多最佳實踐:pingcap.com/blog-cn/#%E…