本文包含:
- TiDB 效能最佳化概述
- TiDB 效能分析和最佳化
- OLTP 負載效能最佳化實踐
- Performance Overview 皮膚重要監控指標詳解
1、TiDB 效能最佳化概述
https://tidb.net/blog/1731b977
2、TiDB 效能分析和最佳化
本文介紹了基於資料庫時間的系統最佳化方法,以及如何利用 TiDB Performance Overview 皮膚進行效能分析和最佳化。
透過本文中介紹的方法,你可以從全域性、自頂向下的角度分析使用者響應時間和資料庫時間,確認使用者響應時間的瓶頸是否在資料庫中。如果瓶頸在資料庫中,你可以透過資料庫時間概覽和 SQL 延遲的分解,定位資料庫內部的瓶頸點,並進行針對性的最佳化。
基於資料庫時間的效能最佳化方法
TiDB 對 SQL 的處理路徑和資料庫時間進行了完善的測量和記錄,方便定位資料庫的效能瓶頸。即使在使用者響應時間的效能資料缺失的情況下,基於 TiDB 資料庫時間的相關效能指標,你也可以達到以下兩個效能分析目標:
- 透過對比 SQL 處理平均延遲和事務中 TiDB 連線的空閒時間,確定整個系統的瓶頸是否在 TiDB 中。
- 如果瓶頸在 TiDB 內部,根據資料庫時間概覽、顏色最佳化法、關鍵指標和資源利用率、自上而下的延遲分解,定位到效能瓶頸具體在整個分散式系統的哪個模組。
確定整個系統的瓶頸是否在 TiDB 中
- 如果事務中 TiDB 連線的平均空閒時間比 SQL 平均處理延遲高,說明應用的事務處理中,主要的延遲不在資料庫中,資料庫時間佔使用者響應時間比例小,可以確認瓶頸不在資料庫中。在這種情況下,需要關注資料庫外部的元件,比如應用伺服器硬體資源是否存在瓶頸,應用到資料庫的網路延遲是否過高等。
- 如果 SQL 平均處理延遲比事務中 TiDB 連線的平均空閒時間高,說明事務中主要的瓶頸在 TiDB 內部,資料庫時間佔使用者響應時間比例大。
如果瓶頸在 TiDB 內部,如何定位
一個典型的 SQL 的處理流程如下所示,TiDB 的效能指標覆蓋了絕大部分的處理路徑,對資料庫時間進行不同維度的分解和上色,使用者可以快速的瞭解負載特性和資料庫內部的瓶頸。
資料庫時間是所有 SQL 處理時間的總和。透過以下三個維度對資料庫時間進行分解,可以幫助你快速定位 TiDB 內部瓶頸:
- 按 SQL 處理型別分解,判斷哪種型別的 SQL 語句消耗資料庫時間最多。對應的分解公式為:
DB Time = Select Time + Insert Time + Update Time + Delete Time + Commit Time + ...
- 按 SQL 處理的 4 個步驟(即 get_token/parse/compile/execute)分解,判斷哪個步驟消耗的時間最多。對應的分解公式為:
DB Time = Get Token Time + Parse Time + Comiple Time + Execute Time
- 對於 execute 耗時,按照 TiDB 執行器本身的時間、TSO 等待時間、KV 請求時間和重試的執行時間,判斷執行階段的瓶頸。對應的分解公式為:
Execute Time ~= TiDB Executor Time + KV Request Time + PD TSO Wait Time + Retried execution time
利用 Performance Overview 皮膚進行效能分析和最佳化
本章介紹如何利用 Grafana 中的 Performance Overview 皮膚進行基於資料庫時間的效能分析和最佳化。
Performance Overview 皮膚按總分結構對 TiDB、TiKV、PD 的效能指標進行編排組織,包括以下三個部分:
- 資料庫時間和 SQL 執行時間概覽:透過顏色標記不同 SQL 型別,SQL 不同執行階段、不同請求的資料庫時間,幫助你快速識別資料庫負載特徵和效能瓶頸。
- 關鍵指標和資源利用率:包含資料庫 QPS、應用和資料庫的連線資訊和請求命令型別、資料庫內部 TSO 和 KV 請求 OPS、TiDB 和 TiKV 的資源使用概況。
- 自上而下的延遲分解:包括 Query 延遲和連線空閒時間對比、Query 延遲分解、execute 階段 TSO 請求和 KV 請求的延遲、TiKV 內部寫延遲的分解等。
資料庫時間和 SQL 執行時間概覽
Database Time 指標為 TiDB 每秒處理 SQL 的延遲總和,即 TiDB 叢集每秒併發處理應用 SQL 請求的總時間(等於活躍連線數)。
Performance Overview 皮膚提供了以下三個面積堆疊圖,幫助你瞭解資料庫負載的型別,快速定位資料庫時間的瓶頸主要是處理什麼語句,集中在哪個執行階段,SQL 執行階段主要等待 TiKV 或者 PD 哪種請求型別。
- Database Time By SQL Type
- Database Time By SQL Phase
- SQL Execute Time Overview
顏色最佳化法
透過觀察資料庫時間分解圖和執行時間概覽圖,你可以直觀地區分正常或者異常的時間消耗,快速定位叢集的異常瓶頸點,高效瞭解叢集的負載特徵。對於正常的時間消耗和請求型別,圖中顯示顏色為綠色系或藍色系。如果非綠色或藍色系的顏色在這兩張圖中佔據了明顯的比例,意味著資料庫時間的分佈不合理。
- Database Time By SQL Type:藍色標識代表 Select 語句,綠色標識代表 Update、Insert、Commit 等 DML 語句。紅色標識代表 General 型別,包含 StmtPrepare、StmtReset、StmtFetch、StmtClose 等命令。
- Database Time By SQL Phase:execute 執行階段為綠色,其他三個階段偏紅色系,如果非綠色的顏色佔比明顯,意味著在執行階段之外資料庫消耗了過多時間,需要進一步分析根源。一個常見的場景是因為無法使用執行計劃快取,導致 compile 階段的橙色佔比明顯。
- SQL Execute Time Overview:綠色系標識代表常規的寫 KV 請求(例如 Prewrite 和 Commit),藍色系標識代表常規的讀 KV 請求(例如 Cop 和 Get),其他色系標識需要注意的問題。例如,悲觀鎖加鎖請求為紅色,TSO 等待為深褐色。如果非藍色系或者非綠色系佔比明顯,意味著執行階段存在異常的瓶頸。例如,當發生嚴重鎖衝突時,紅色的悲觀鎖時間會佔比明顯;當負載中 TSO 等待的消耗時間過長時,深褐色會佔比明顯。
示例 1:TPC-C 負載
Database Time by SQL Type:主要消耗時間的語句為 commit、update、select 和 insert 語句。
- Database Time by SQL Phase:主要消耗時間的階段為綠色的 execute 階段。
- SQL Execute Time Overview:執行階段主要消耗時間的 KV 請求為綠色的 Prewrite 和 Commit。
注意
KV 請求的總時間大於 execute time 為正常現象,因為 TiDB 執行器可能併發向多個 TiKV 傳送 KV 請求,導致總的 KV 請求等待時間大於 execute time。TPC-C 負載中,事務提交時,TiDB 會向多個 TiKV 並行傳送 Prewrite 和 Commit 請求,所以這個例子中 Prewrite、Commit 和 PessimisticsLock 請求的總時間明顯大於 execute time。
execute time 也可能明顯大於 KV 請求的總時間加上 tso_wait 的時間,這意味著 SQL 執行階段主要時間花在 TiDB 執行器內部。兩種常見的例子:
例 1:TiDB 執行器從 TiKV 讀取大量資料之後,需要在 TiDB 內部進行復雜的關聯和聚合,消耗大量時間。
例 2:應用的寫語句鎖衝突嚴重,頻繁鎖重試導致
Retried execution time
過長。
示例 2:OLTP 讀密集負載
Database Time by SQL Type:主要消耗時間的語句為 select、commit、update和 insert 語句。其中,select 佔據絕大部分的資料庫時間。
- Database Time by SQL Phase:主要消耗時間的階段為綠色的 execute 階段。
- SQL Execute Time Overview:執行階段主要消耗時間為深褐色的 pd tso_wait、藍色的 KV Get 和綠色的 Prewrite 和 Commit。
示例 3:只讀 OLTP 負載
- Database Time by SQL Type:幾乎所有語句為 select。
- Database Time by SQL Phase:主要消耗時間的階段為橙色的 compile 和綠色的 execute 階段。compile 階段延遲最高,代表著 TiDB 生成執行計劃的過程耗時過長,需要根據後續的效能資料進一步確定根源。
- SQL Execute Time Overview:執行階段主要消耗時間的 KV 請求為藍色 BatchGet。
注意
示例 3 select 語句需要從多個 TiKV 並行讀取幾千行資料,BatchGet 請求的總時間遠大於執行時間。
示例 4: 鎖爭用負載
Database Time by SQL Type:主要為 Update 語句。
- Database Time by SQL Phase:主要消耗時間的階段為綠色的 execute 階段。
- SQL Execute Time Overview:執行階段主要消耗時間的 KV 請求為紅色的悲觀鎖 PessimisticLock,execute time 明顯大於 KV 請求的總時間,這是因為應用的寫語句鎖衝突嚴重,頻繁鎖重試導致
Retried execution time
過長。目前Retried execution time
消耗的時間,TiDB 尚未進行測量。
TiDB 關鍵指標和叢集資源利用率
Query Per Second、Command Per Second 和 Prepared-Plan-Cache
透過觀察 Performance Overview 裡的以下三個皮膚,可以瞭解應用的負載型別,與 TiDB 的互動方式,以及是否能有效地利用 TiDB 的執行計劃快取。
QPS:表示 Query Per Second,包含應用的 SQL 語句型別執行次數分佈。
CPS By Type:CPS 表示 Command Per Second,Command 代表 MySQL 協議的命令型別。同樣一個查詢語句可以透過 query 或者 prepared statement 的命令型別傳送到 TiDB。
Queries Using Plan Cache OPS:TiDB 叢集每秒命中執行計劃快取的次數。執行計劃快取只支援 prepared statement 命令。TiDB 開啟執行計劃快取的情況下,存在三種使用情況:
完全無法命中執行計劃快取:每秒命中次數為 0,因為應用使用 query 命令,或者每次 StmtExecute 執行之後呼叫 StmtClose 命令,導致快取的執行計劃被清理。
完全命中執行計劃快取:每秒命中次數等於 StmtExecute 命令每秒執行次數。
部分命中執行計劃快取:每秒命中次數小於 StmtExecute 命令每秒執行次數,執行計劃快取目前存在一些限制,比如不支援子查詢,該型別的 SQL 執行計劃無法被快取。
示例 1:TPC-C 負載
TPC-C 負載型別主要以 Update、Select 和 Insert 語句為主。總的 QPS 等於每秒 StmtExecute 的次數,並且 StmtExecute 每秒的資料基本等於 Queries Using Plan Cache OPS。這是 OLTP 負載理想的情況,客戶端執行使用 prepared statement,並且在客戶端快取了 prepared statement 物件,執行每條 SQL 語句時直接呼叫 statement 執行。執行時都命中執行計劃快取,不需要重新 compile 生成執行計劃。
示例 2:只讀 OLTP 負載,使用 query 命令無法使用執行計劃快取
這個負載中, Commit QPS = Rollback QPS = Select QPS。應用開啟了 auto-commit 併發,每次從連線池獲取連線都會執行 rollback,因此這三種語句的執行次數是相同的。
QPS 皮膚中出現的紅色加粗線為 Failed Query,座標的值為右邊的 Y 軸。非 0 代表此負載中存在錯誤語句。
- 總的 QPS 等於 CPS By Type 皮膚中的 Query,說明應用中使用了 query 命令。
- Queries Using Plan Cache OPS 皮膚沒有資料,因為不使用 prepared statement 介面,無法使用 TiDB 的執行計劃快取, 意味著應用的每一條 query,TiDB 都需要重新解析,重新生成執行計劃。通常會導致 compile 時間變長以及 TiDB CPU 消耗的增加。
示例 3:OLTP 負載,使用 prepared statement 介面無法使用執行計劃快取
StmtPreare 次數 = StmtExecute 次數 = StmtClose 次數 ~= StmtFetch 次數,應用使用了 prepare > execute > fetch > close 的 loop,很多框架都會在 execute 之後呼叫 close,確保資源不會洩露。這會帶來兩個問題:
- 執行每條 SQL 語句需要 4 個命令,以及 4 次網路往返。
- Queries Using Plan Cache OPS 為 0, 無法命中執行計劃快取。StmtClose 命令預設會清理快取的執行計劃,導致下一次 StmtPreare 命令需要重新生成執行計劃。
注意
從 TiDB v6.0.0 起,你可以透過全域性變數 ( set global tidb_ignore_prepared_cache_close_stmt=on;
) 控制 StmtClose 命令不清理已被快取的執行計劃,使得下一次的 SQL 的執行不需要重新生成執行計劃。
KV/TSO Request OPS 和連線資訊
在 KV/TSO Request OPS 皮膚中,你可以檢視 KV 和 TSO 每秒請求的資料統計。其中, kv request total
代表 TiDB 到 TiKV 所有請求的總和。透過觀察 TiDB 到 PD 和 TiKV 的請求型別,可以瞭解叢集內部的負載特徵。
在 Connection Count (連線資訊)皮膚中,你可以檢視總的連線數和每個 TiDB 的連線數,並由此判斷連線總數是否正常,每個 TiDB 的連線數是否不均衡。 active connections
記錄著活躍連線數,等於每秒的資料庫時間。
示例 1:繁忙的負載
在此 TPC-C 負載中:
- 每秒總的 KV 請求的數量為 104.2k。按請求數量排序,最高的請求型別為
PessimisticsLock
、Prewrite
、Commit
和BatchGet
等。 - 總的連線數為 810,三個 TiDB 例項的連線數大體均衡。活躍的連線數為 787.1。對比活躍連線數和總連線數,97% 的連線都是活躍的,通常意味著資料庫是這個應用系統的效能瓶頸。
示例 2:空閒的負載
在此負載中:
- 每秒總的 KV 請求資料是 2.6 K,TSO 請求次數是每秒 1.1 K。
- 總的連線數為 410,連線數在三個 TiDB 中相對均衡。活躍連線數只有 2.5,說明資料庫系統非常空閒。
TiDB CPU,以及 TiKV CPU 和 IO 使用情況
在 TiDB CPU 和 TiKV CPU/IO MBps 這兩個皮膚中,你可以觀察到 TiDB 和 TiKV 的邏輯 CPU 使用率和 IO 吞吐,包含平均、最大和 delta(最大 CPU 使用率減去最小 CPU 使用率),從而用來判定 TiDB 和 TiKV 總體的 CPU 使用率。
- 透過
delta
值,你可以判斷 TiDB 是否存在 CPU 使用負載不均衡(通常伴隨著應用連線不均衡),TiKV 是否存在熱點。 - 透過 TiDB 和 TiKV 的資源使用概覽,你可以快速判斷叢集是否存在資源瓶頸,最需要擴容的元件是 TiDB 還是 TiKV。
示例 1:TiDB 資源使用率高
下圖負載中,每個 TiDB 和 TiKV 配置 8 CPU。
- TiDB 平均 CPU 為 575%。最大 CPU 為 643%,delta CPU 為 136%。
- TiKV 平均 CPU 為 146%,最大 CPU 215%。delta CPU 為 118%。TiKV 的平均 IO 吞吐為 9.06 MB/s,最大 IO 吞吐為 19.7 MB/s,delta IO 吞吐 為 17.1 MB/s。
由此可以判斷,TiDB 的 CPU 消耗明顯更高,並接近於 8 CPU 的瓶頸,可以考慮擴容 TiDB。
示例 2:TiKV 資源使用率高
下圖 TPC-C 負載中,每個 TiDB 和 TiKV 配置 16 CPU。
- TiDB 平均 CPU 為 883%。最大 CPU 為 962%,delta CPU 為 153%。
- TiKV 平均 CPU 為 1288%,最大 CPU 1360%。delta CPU 為 126%。TiKV 的平均 IO 吞吐為130 MB/s,最大 IO 吞吐為 153 MB/s,delta IO 吞吐為 53.7 MB/s。
由此可以判斷,TiKV 的 CPU 消耗更高,因為 TPC-C 是一個寫密集場景,這是正常現象,可以考慮擴容 TiKV 節點提升效能。
Query 延遲分解和關鍵的延遲指標
延遲皮膚通常包含平均值和 99 分位數,平均值用來定位總體的瓶頸,99 分位數用來判斷是否存在延遲嚴重抖動的情況。判斷效能抖動範圍時,可能還需要需要藉助 999 分位數。
Duration 和 Connection Idle Duration
Duration 皮膚包含了所有語句的 99 延遲和每種 SQL 型別的平均延遲。Connection Idle Duration 皮膚包含連線空閒的平均和 99 延遲,連線空閒時包含兩種狀態:
- in-txn:代表事務中連線的空閒時間,即當連線處於事務中時,處理完上一條 SQL 之後,收到下一條 SQL 語句的間隔時間。
- not-in-txn:當連線沒有處於事務中,處理完上一條 SQL 之後,收到下一條 SQL 語句的間隔時間。
應用進行資料庫事務時,通常使用同一個資料庫連線。對比 query 的平均延遲和 connection idle duration 的延遲,可以判斷整個系統效能瓶頸或者使用者響應時間的抖動是否是由 TiDB 導致的。
- 如果應用負載不是隻讀的,包含事務,對比 query 平均延遲和
avg-in-txn
可以判斷應用處理事務時,主要的時間是花在資料庫內部還是在資料庫外面,藉此定位使用者響應時間的瓶頸。 - 如果是隻讀負載,不存在
avg-in-txn
指標,可以對比 query 平均延遲和avg-not-in-txn
指標。
現實的客戶負載中,瓶頸在資料庫外面的情況並不少見,例如:
- 客戶端伺服器配置過低,CPU 資源不夠。
- 使用 HAProxy 作為 TiDB 叢集代理,但是 HAProxy CPU 資源不夠。
- 使用 HAProxy 作為 TiDB 叢集代理,但是高負載下 HAProxy 伺服器的網路頻寬被打滿。
- 應用伺服器到資料庫延遲過高,比如公有云環境應用和 TiDB 叢集不在同一個地區,比如資料庫的 DNS 均衡器和 TiDB 叢集不在同一個地區。
- 客戶端程式存在瓶頸,無法充分利用伺服器的多 CPU 核或者多 Numa 資源,比如應用只使用一個 JVM 向 TiDB 建立上千個 JDBC 連線。
示例 1:使用者響應時間的瓶頸在 TiDB 中
在此 TPC-C 負載中:
- 所有 SQL 語句的平均延遲 477 us,99 延遲 3.13ms。平均 commit 語句 2.02ms,平均 insert 語句 609ms,平均查詢語句 468us。
- 事務中連線空閒時間
avg-in-txn
171 us。
由此可以判斷,平均的 query 延遲明顯大於 avg-in-txn
,說明事務處理中,主要的瓶頸在資料庫內部。
示例 2:使用者響應時間的瓶頸不在 TiDB 中
在此負載中,平均 query 延遲為 1.69ms,事務中連線空閒時間 avg-in-txn
為 18ms。說明事務中,TiDB 平均花了 1.69ms 處理完一個 SQL 語句之後,需要等待 18ms 才能收到下一條語句。
由此可以判斷,使用者響應時間的瓶頸不在 TiDB 中。這個例子是在一個公有云環境下,因為應用和資料庫不在同一個地區,應用和資料庫之間的網路延遲高導致了超高的連線空閒時間。
Parse、Compile 和 Execute Duration
在 TiDB 中,從輸入查詢文字到返回結果的典型處理流程 1。
SQL 在 TiDB 內部的處理分為四個階段,get token、parse、compile 和 execute:
- get token 階段:通常只有幾微秒的時間,可以忽略。除非 TiDB 單個例項的連線數達到的 token-limit 的限制,建立連線的時候被限流。
- parse 階段:query 語句解析為抽象語法樹 abstract syntax tree (AST)。
- compile 階段:根據 parse 階段輸出的 AST 和統計資訊,編譯出執行計劃。整個過程主要步驟為邏輯最佳化與物理最佳化,前者透過一些規則對查詢計劃進行最佳化,例如基於關係代數的列裁剪等,後者透過統計資訊和基於成本的最佳化器,對執行計劃的成本進行估算,並選擇整體成本最小的物理執行計劃。
- execute 階段:時間消耗視情況,先等待全域性唯一的時間戳 TSO,之後執行器根據執行計劃中運算元涉及的 Key 範圍,構建出 TiKV 的 API 請求,分發到 TiKV。execute 時間包含 TSO 等待時間、KV 請求的時間和 TiDB 執行器本身處理資料的時間。
如果應用統一使用 query 或者 StmtExecute MySQL 命令介面,可以使用以下公式來定位平均延遲的瓶頸。
avg Query Duration = avg Get Token + avg Parse Duration + avg Compile Duration + avg Execute Duration
通常 execute 階段會佔 query 延遲的主要部分,在以下情況下,parse 和 compile 階段也會佔比明顯。
- parse 階段延遲佔比明顯:比如 query 語句很長,文字解析消耗大量的 CPU。
- compile 階段延遲佔比明顯:如果應用沒有使用執行計劃快取,每個語句都需要生成執行計劃。compile 階段的延遲可能達到幾毫秒或者幾十毫秒。如果無法命中執行計劃快取,compile 階段需要進行邏輯最佳化和物理最佳化,這將消耗大量的 CPU 和記憶體,並給 Go Runtime 帶來壓力(因為 TiDB 是
href="https://go.dev/">Go 編寫的),進一步影響 TiDB 其他元件的效能。這說明,OLTP 負載在 TiDB 中是否能高效執行,執行計劃緩扮演了重要的角色。
示例 1:資料庫瓶頸在 compile 階段
此圖中 parse、compile 和 execute 階段的平均時間分別為 17.1 us、729 us 和 681 us。因為應用使用 query 命令介面,無法使用執行計劃快取,所以 compile 階段延遲高。
示例 2:資料庫瓶頸在 execute 階段
在此 TPC-C 負載中,parse、compile 和 execute 階段的平均時間分別為 7.39us、38.1us 和 12.8ms。query 延遲的瓶頸在於 execute 階段。
KV 和 TSO Request Duration
在 execute 階段,TiDB 會跟 PD 和 TiKV 進行互動。如下圖所示,當 TiDB 處理 SQL 語句請求時,在進行 parse 和 compile 之前,如果需要獲取 TSO,會先請求生成 TSO。PD Client 不會阻塞呼叫者,而是直接返回一個 TSFuture
,並在後臺非同步處理 TSO 請求的收發,一旦完成立即返回給 TSFuture,TSFuture 的持有者則需要呼叫 Wait 方法來獲得最終的 TSO 結果。當 TiDB 完成 parse 和 compile 之後, 進入 execute 階段,此時存在兩個情況:
- 如果 TSO 請求已經完成,Wait 方法會立刻返回一個可用的 TSO 或 error
- 如果 TSO 請求還未完成,Wait 方法會 block 住等待一個可用的 TSO 或 error(說明 gRPC 請求已傳送但尚未收到返回結果,網路延遲較高)
TSO 等待的時間記錄為 TSO WAIT,TSO 請求的網路時間記錄為 TSO RPC。TiDB TSO 等待完成之後,執行過程中通常需要和 TiKV 進行讀寫互動:
- 讀的 KV 請求常見型別:Get、BatchGet 和 Cop
- 寫的 KV 請求常見型別:PessimisticLock,二階段提交的 Prewrite 和 Commit
這一部分的指標對應以下三個皮膚:
- Avg TiDB KV Request Duration:TiDB 測量的 KV 請求的平均延遲
- Avg TiKV GRPC Duration:TiKV 內部 GRPC 訊息處理的平均延遲
- PD TSO Wait/RPC Duration:TiDB 執行器等待 TSO 延遲 (wait) 和 TSO 請求的網路延遲(rpc)。
其中,Avg TiDB KV Request Duration 和 Avg TiKV GRPC Duration 的關係如下
Avg TiDB KV Request Duration = Avg TiKV GRPC Duration + TiDB 與 TiKV 之間的網路延遲 + TiKV GRPC 處理時間 + TiDB GRPC 處理時間和排程延遲。
Avg TiDB KV Request Duration 和 Avg TiKV GRPC Duration 的差值跟網路流量和延遲,TiDB 和 TiKV 的資源使用情況密切相關。
- 同一個機房內,Avg TiDB KV Request Duration 和 Avg TiKV GRPC Duration 的差值通常應該小於 2 毫秒。
- 同一地區的不同可用區,Avg TiDB KV Request Duration 和 Avg TiKV GRPC Duration 的差值通常應該小於 5 毫秒。
示例 1:同機器低負載的叢集
在此負載中,TiDB 側平均 Prewrite 請求延遲為 925 us,TiKV 內部 kv_prewrite 平均處理延遲為 720 us,相差 200 us 左右,是同機房內正常的延遲。TSO wait 平均延遲 206 us,rpc 時間為 144 us。
示例 2:公有云叢集,負載正常
在此示例中,TiDB 叢集部署在同一個地區的不同機房。TiDB 側平均 Commit 請求延遲為 12.7 ms,TiKV 內部 kv_commit 平均處理延遲為 10.2ms,相差 2.5ms 左右。TSO wait 平均延遲為 3.12ms,rpc 時間為 693us。
示例 3:公有云叢集,資源嚴重過載
在此示例中,TiDB 叢集部署在同一個地區的不同機房,TiDB 網路和 CPU 資源嚴重過載。TiDB 側平均 BatchGet 請求延遲為 38.6 ms,TiKV 內部 kv_batch_get 平均處理延遲為 6.15 ms,相差超過 32 ms,遠高於正常值。TSO wait 平均延遲為 9.45ms,rpc 時間為 14.3ms。
Storage Async Write Duration、Store Duration 和 Apply Duration
TiKV 對於寫請求的處理流程如下圖
scheduler worker
會先處理寫請求,進行事務一致性檢查,並把寫請求轉化成鍵值對,傳送到raftstore
模組。raftstore
為 TiKV 的共識模組,使用 Raft 共識演算法,使多個 TiKV 組成的儲存層可以容錯。Raftstore 分為 store 執行緒和 apply 執行緒。:store 執行緒負載處理 Raft 訊息和新的
proposals
。 當收到新的proposals
時,leader 節點的 store 執行緒會寫入本地 Raft DB,並將訊息複製到多個 follower 節點。當這個proposals
在多數例項持久化成功之後,proposals
成功被提交。apply 執行緒會負載將提交的內容寫入到 KV DB 中。當寫操作的內容被成功的寫入 KV 資料庫中,apply 執行緒會通知外層請求寫請求已經完成。
Storage Async Write Duration 指標記錄寫請求進入 raftstore 之後的延遲,採集的粒度具體到每個請求的級別。
Storage Async Write Duration 分為 Store Duration 和 Apply Duration。你可以透過以下公式定位寫請求的瓶頸主要是 在 Store 還是 Apply 步驟。
avg Storage Async Write Duration = avg Store Duration + avg Apply Duration
注意
Store Duration 和 Apply Duration 從 v5.3.0 版本開始支援。
示例 1:同一個 OLTP 負載在 v5.3.0 和 v5.4.0 版本的對比
v5.4.0 版本,一個寫密集的 OLTP 負載 QPS 比 v5.3.0 提升了 14%。應用以上公式
- v5.3.0:24.4 ms ~= 17.7 ms + 6.59 ms
- v5.4.0:21.4 ms ~= 14.0 ms + 7.33 ms
因為 v5.4.0 版本中,TiKV 對 gRPC 模組進行了最佳化,最佳化了 Raft 日誌複製速度, 相比 v5.3.0 降低了 Store Duration。
v5.3.0:
v5.4.0:
示例 2:Store Duration 瓶頸明顯
應用以上公式:10.1 ms ~= 9.81 ms + 0.304 ms,說明寫請求的延遲瓶頸在 Store Duration。
Commit Log Duration、Append Log Duration 和 Apply Log Duration
Commit Log Duration、Append Log Duration 和 Apply Log Duration 這三個延遲是 raftstore 內部關鍵操作的延遲記錄。這些記錄採集的粒度是 batch 操作級別的,每個操作會把多個寫請求合併在一起,因此不能直接對應上文的 Store Duration 和 Apply Duration。
- Commit Log Duration 和 Append Log Duration 均為 store 執行緒的操作。Commit Log Duration 包含複製 Raft 日誌到其他 TiKV 節點,保證 raft-log 的持久化。一般包含兩次 Append Log Duration,一次 leader,一次 follower 的。Commit Log Duration 延遲通常會明顯高於 Append Log Duration,因為包含了透過網路複製 Raft 日誌到其他 TiKV 的時間。
- Apply Log Duration 記錄了 apply 執行緒 apply Raft 日誌的延遲。
Commit Log Duration 慢的常見場景:
- TiKV CPU 資源存在瓶頸,排程延遲高
raftstore.store-pool-size
設定過小或者過大(過大也可能導致效能下降)- IO 延遲高,導致 Append Log Duration 延遲高
- TiKV 之間的網路延遲比較高
- TiKV 的 gRPC 執行緒數設定過小或者多個 gRPC CPU 資源使用不均衡
Apply Log Duration 慢的常見場景:
- TiKV CPU 資源存在瓶頸,排程延遲高
raftstore.apply-pool-size
設定過小或者過大(過大也可能導致效能下降)- IO 延遲比較高
示例 1:同一個 OLTP 負載在 v5.3.0 和 v5.4.0 版本的對比
v5.4.0 版本,一個寫密集的 OLTP 負載 QPS 比 v5.3.0 提升了 14%。 對比這三個關鍵延遲:
Avg Duration | v5.3.0(ms) | v5.4.0(ms) |
---|---|---|
Append Log Duration | 0.27 | 0.303 |
Commit Log Duration | 13 | 8.68 |
Apply Log Duration | 0.457 | 0.514 |
因為 v5.4.0 版本中,TiKV 對 gRPC 模組進行了最佳化,最佳化了 Raft 日誌複製速度, 相比 v5.3.0 降低了 Commit Log Duration 和 Store Duration。
v5.3.0:
v5.4.0:
示例 2:Commit Log Duration 瓶頸明顯的例子
平均 Append Log Duration = 4.38 ms
- 平均 Commit Log Duration = 7.92 ms
- 平均 Apply Log Duration = 172 us
Store 執行緒的 Commit Log Duration 明顯比 Apply Log Duration 高,並且 Append Log Duration 比 Apply Log Duration 明顯的高,說明 Store 執行緒在 CPU 和 IO 都可能都存在瓶頸。可能降低 Commit Log Duration 和 Append Log Duration 的方式如下:
- 如果 TiKV CPU 資源充足,考慮增加 Store 執行緒,即
raftstore.store-pool-size
。 - 如果 TiDB 為 v5.4.0 及之後的版本,考慮啟用
[Raft Engine ](https://zhuanlan.zhihu.com/p/ht%3C/code%3Etps://docs.pingcap.com/zh/tidb/stable/tikv-configuration-file#raft-engine),Raft Engine 具有更輕量的執行路徑,在一些場景下顯著減少 IO 寫入量和寫入請求的長尾延遲,啟用方式為設定:
raft-engine.enable: true`` - 如果 TiKV CPU 資源充足,且 TiDB 為 v5.3.0 及之後的版本,考慮啟用
[StoreWriter ](https://zhuanlan.zhihu.com/p/ht%3C/code%3Etps://docs.pingcap.com/zh/tidb/stable/tune-tikv-thread-performance#tikv-%E7%BA%BF%E7%A8%8B%E6%B1%A0%E8%B0%83%E4%BC%98)。啟用方式:
raftstore.store-io-pool-size: 1。
低於 v6.1.0 的 TiDB 版本如何使用 Performance overview 皮膚
從 v6.1.0 起,TiDB Grafana 元件預設內建了 Performance Overview 皮膚。Performance overview 皮膚相容 TiDB v4.x 和 v5.x 版本。如果你的 TiDB 版本低於 v6.1.0,需要手動匯入 [performance_overview.json 3](https://link.zhihu.com/?target=https%3A//github.c%3C/code%3Eom/pingcap/tidb/blob/master/metrics/grafana/performance_overview.json)。
匯入方法如圖所示:
3、OLTP 負載效能最佳化實踐
https://tidb.net/blog/f1637cfd
4、Performance Overview 皮膚重要監控指標詳解
https://tidb.net/blog/90492e9b
本作品採用《CC 協議》,轉載必須註明作者和本文連結