TiDB 慢查詢日誌分析

導讀

TiDB 中的慢查詢日誌是一項 關鍵的性能監控工具,其主要作用在於協助數據庫管理員追蹤執行時間較長的 SQL 查詢語句。 通過記錄那些超過設定閾值的查詢,慢查詢日誌爲性能優化提供了關鍵的線索,有助於發現潛在的性能瓶頸,優化索引以及重構查詢語句,從而提升數據庫的整體性能。 本文將主要介紹 TiDB 中慢查詢日誌的功能,並探討常用的慢查詢日誌分析方法 。

本文作者 :王勇,中金公司信息技術部高級架構師,負責中金公司盤古 PaaS 、中間件、數據庫規劃建設以及公司整體信息技術應用創新、開源治理工作,助力多個投行核心系統國產化落地。

慢查詢相關參數

  • tidb_enable_slow_log :用於控制是否開啓 slow log 功能。
  • tidb_slow_log_threshold :設置慢日誌的閾值,執行時間超過閾值的 SQL 語句將被記錄到慢日誌中。默認值是 300 ms。
  • tidb_query_log_max_len :設置慢日誌記錄 SQL 語句的最大長度。默認值是 4096 byte。
  • tidb_redact_log :設置慢日誌記錄 SQL 時是否將用戶數據脫敏用 ? 代替。默認值是 0 ,即關閉該功能。
  • tidb_enable_collect_execution_info :設置是否記錄執行計劃中各個算子的物理執行信息,默認值是 1 。

慢查詢日誌原理

TiDB 的慢查詢日誌原理與 MySQL 一致,在每條 SQL 執行結束時,並且執行時間超過慢日誌閾值時,會把 SQL 執行相關信息記錄到慢日誌中,同樣的 SQL 多次執行超過閾值都會記錄。

分析慢查詢日誌

由於 TiDB 是採用存算分離架構的分佈式數據庫,在這種架構下,每個 TiDB Server 節點都會產生慢日誌。爲方便查詢慢日誌,TiDB 提供了內存映射表 INFORMATION_SCHEMA.SLOW_QUERY ,並在 TiDB Dashboard 中提供專門的界面用於搜索和查看慢查詢日誌。官方文檔中也提供了多種常見的慢查詢日誌查詢語句,參考:慢查詢日誌 ( https://docs.pingcap.com/zh/tidb/v7.1/identify-slow-queries#查詢-slow_querycluster_slow_query-示例 )。

然而,在系統高負載或異常情況下,短時間內生成過多慢 SQL 導致慢 SQL 變得難以分析,這也是像 MySQL 等數據庫提供慢日誌分析工具的原因,例如 mysqldumpslow 、 pt-query-digest 等工具。這些工具通常以某種聚合的方式輸出結果,使結果更加清晰易懂。借鑑這些工具的思路,筆者開發了一條常用的慢日誌分析 SQL,以更便捷地處理慢查詢日誌。

1 慢日誌聚合查詢 SQL

-- 慢查詢日誌,聚合查詢
WITH ss AS
(SELECT s.Digest ,s.Plan_digest,
count(1) exec_count,
sum(s.Succ) succ_count,
round(sum(s.Query_time),4) sum_query_time,
round(avg(s.Query_time),4) avg_query_time,
sum(s.Total_keys) sum_total_keys,
avg(s.Total_keys) avg_total_keys,
sum(s.Process_keys) sum_process_keys,
avg(s.Process_keys) avg_process_keys,
min(s.`Time`) min_time,
max(s.`Time`) max_time,
round(max(s.Mem_max)/1024/1024,4) Mem_max,
round(max(s.Disk_max)/1024/1024,4) Disk_max,
avg(s.Result_rows) avg_Result_rows,
max(s.Result_rows) max_Result_rows,
sum(Plan_from_binding) Plan_from_binding
FROM information_schema.cluster_slow_query s
WHERE s.time>=adddate(now(),INTERVAL -1 DAY)
AND s.time<=now()
AND s.Is_internal =0
-- AND UPPER(s.query) NOT LIKE '%ANALYZE TABLE%'
-- AND UPPER(s.query) NOT LIKE '%DBEAVER%'
-- AND UPPER(s.query) NOT LIKE '%ADD INDEX%'
-- AND UPPER(s.query) NOT LIKE '%CREATE INDEX%'
GROUP BY s.Digest ,s.Plan_digest
ORDER BY sum(s.Query_time) desc
LIMIT 35)
SELECT ss.Digest,         -- SQL Digest
ss.Plan_digest,           -- PLAN Digest
(SELECT s1.Query FROM information_schema.cluster_slow_query s1 WHERE s1.Digest=ss.digest AND s1.time>=ss.min_time AND s1.time<=ss.max_time LIMIT 1) query,  -- SQL文本
(SELECT s2.plan FROM information_schema.cluster_slow_query s2 WHERE s2.Plan_digest=ss.plan_digest AND s2.time>=ss.min_time AND s2.time<=ss.max_time LIMIT 1) plan, -- 執行計劃
ss.exec_count,            -- SQL總執行次數
ss.succ_count,            -- SQL執行成功次數
ss.sum_query_time,        -- 總執行時間(秒)
ss.avg_query_time,        -- 平均單次執行時間(秒)
ss.sum_total_keys,        -- 總掃描key數量
ss.avg_total_keys,        -- 平均單次掃描key數量
ss.sum_process_keys,      -- 總處理key數量
ss.avg_process_keys,      -- 平均單次處理key數量
ss.min_time,              -- 查詢時間段內第一次SQL執行結束時間
ss.max_time,              -- 查詢時間段內最後一次SQL執行結束時間
ss.Mem_max,               -- 單次執行中內存佔用最大值(MB)
ss.Disk_max,              -- 單次執行中磁盤佔用最大值(MB)
ss.avg_Result_rows,       -- 平均返回行數
ss.max_Result_rows,       -- 單次最大返回行數
ss.Plan_from_binding      -- 走SQL binding的次數
FROM ss;

這條 SQL 是筆者常用的一條慢查詢分析語句,大家可以根據個人需要靈活地調整排序字段、查詢字段和查詢條件,以滿足不同場景下的分析需求。

在這個 SQL 中,query 和 plan 字段是使用標量子查詢的方式獲取。經過測試,這種寫法相比直接使用 group by,能夠節省大量內存,所以能夠分析更長時間段的慢查詢。

既然是聚合查詢,爲什麼不直接用 statements_summary_history 表呢?筆者覺得有三點原因,一是 statements_summary_history 由於本身是半小時的聚合數據,在應對短時間段的性能分析時可能不夠精細。二是早期版本的 statements_summary_history 是純內存表,可能由於 TiDB Server OOM 重啓而導致數據丟失,而慢查詢日誌是存儲在文件中的,因此 TiDB Server OOM 重啓不會導致慢查詢日誌丟失。三是 statements_summary_history 有容量限制,記錄的 SQL 可能被驅逐出去,而慢查詢日誌默認記錄超過 300 毫秒的查詢,已滿足分析需求了。

2 單條 SQL 執行歷史

SELECT 
date_format(adddate(s.Time,interval - s.Query_time second),'%Y-%m-%d %H') sql_exec_start,
count(1) exec_cnt,
sum(s.Succ) succ_cnt,
count(distinct s.Plan_digest) plan_cnt,
case when count(distinct s.Plan_digest)<5 then group_concat(distinct substr( s.Plan_digest,1,4)) else null end plan_digest,
round(sum(s.Query_time),4) sum_q_time,
round(avg(s.Query_time),4) avg_q_time,
sum(s.Total_keys) sum_t_keys,
round(avg(s.Total_keys),4) avg_t_keys,
sum(s.Process_keys) sum_p_keys,
avg(s.Process_keys) avg_p_keys,
round(max(s.Mem_max/1024/1024),2) Mem_max_m,
round(max(s.Disk_max/1024/1024),2) Disk_max_m,
round(avg(s.Result_rows),4) avg_rows,
max(s.Result_rows) max_rows,
sum(Plan_from_binding) PFB
from information_schema.cluster_slow_query s
where s.digest='a0adeeb79b71315ac13a77f3f11162106b5ec7b48212cf17c20c754263ab9228'
and time>=adddate(now(),interval -3 day)
and time<=now()
group by date_format(adddate(s.Time,interval - s.Query_time second),'%Y-%m-%d %H')
order by 1 desc;

這條 SQL 是筆者常用的另一條慢查詢分析語句,用於分析單個 SQL 的歷史執行情況。通過這個查詢,可以清晰地瞭解特定 SQL 在歷次執行中的變化,包括執行計劃、掃描數據量、執行時間等方面的情況。

收集慢查詢日誌腳本

這個腳本用於生成 HTML 格式的慢日誌分析結果,結合定時任務和 Nginx 的自動索引功能,可以輕鬆地收集和查看各個 TiDB 集羣的慢日誌。

腳本請在這個鏈接取: https://asktug.com/t/topic/1022684

效果展示:

總結

本文闡述了 TiDB 慢查詢日誌的相關配置和原理,並分享了筆者在實際工作中使用的慢查詢日誌分析 SQL。爲讀者提供了一種實際而有效的慢查詢日誌分析思路。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章