TiDB 效能分析&效能調優&最佳化實踐大全

TiDBCommunityTechPortal發表於2022-06-20

本文包含:

  • TiDB 效能最佳化概述
  • TiDB 效能分析和最佳化
  • OLTP 負載效能最佳化實踐
  • Performance Overview 皮膚重要監控指標詳解

1、TiDB 效能最佳化概述

https://tidb.net/blog/1731b977

2、TiDB 效能分析和最佳化

本文介紹了基於資料庫時間的系統最佳化方法,以及如何利用 TiDB Performance Overview 皮膚進行效能分析和最佳化。

透過本文中介紹的方法,你可以從全域性、自頂向下的角度分析使用者響應時間和資料庫時間,確認使用者響應時間的瓶頸是否在資料庫中。如果瓶頸在資料庫中,你可以透過資料庫時間概覽和 SQL 延遲的分解,定位資料庫內部的瓶頸點,並進行針對性的最佳化。

基於資料庫時間的效能最佳化方法

TiDB 對 SQL 的處理路徑和資料庫時間進行了完善的測量和記錄,方便定位資料庫的效能瓶頸。即使在使用者響應時間的效能資料缺失的情況下,基於 TiDB 資料庫時間的相關效能指標,你也可以達到以下兩個效能分析目標:

  1. 透過對比 SQL 處理平均延遲和事務中 TiDB 連線的空閒時間,確定整個系統的瓶頸是否在 TiDB 中。
  2. 如果瓶頸在 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。按請求數量排序,最高的請求型別為 PessimisticsLockPrewriteCommitBatchGet 等。
  • 總的連線數為 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 協議》,轉載必須註明作者和本文連結

相關文章