diff --git a/statement-summary-tables.md b/statement-summary-tables.md index 03725ced087b..36b80ea299ea 100644 --- a/statement-summary-tables.md +++ b/statement-summary-tables.md @@ -8,11 +8,18 @@ aliases: ['/docs-cn/dev/reference/performance/statement-summary/'] 针对 SQL 性能相关的问题,MySQL 在 `performance_schema` 提供了 [statement summary tables](https://dev.mysql.com/doc/refman/5.6/en/statement-summary-tables.html),用来监控和统计 SQL。例如其中的一张表 `events_statements_summary_by_digest`,提供了丰富的字段,包括延迟、执行次数、扫描行数、全表扫描次数等,有助于用户定位 SQL 问题。 -为此,从 3.0.4 版本开始,TiDB 也提供系统表 `events_statements_summary_by_digest`,从 3.0.8 开始提供系统表 `events_statements_summary_by_digest_history`。本文将详细介绍这两张表,以及如何利用它们来排查 SQL 性能问题。 +为此,从 4.0.0-rc.1 版本开始,TiDB 在 `information_schema` 中提供与 `events_statements_summary_by_digest` 功能相似的系统表: -## `events_statements_summary_by_digest` +- `statements_summary` +- `statements_summary_history` +- `cluster_statements_summary` +- `cluster_statements_summary_history` -`events_statements_summary_by_digest` 是 `performance_schema` 里的一张系统表,它把 SQL 按 SQL digest 和 plan digest 分组,统计每一组的 SQL 信息。 +本文将详细介绍这些表,以及如何利用它们来排查 SQL 性能问题。 + +## `statements_summary` + +`statements_summary` 是 `information_schema` 里的一张系统表,它把 SQL 按 SQL digest 和 plan digest 分组,统计每一组的 SQL 信息。 此处的 SQL digest 与 slow log 里的 SQL digest 一样,是把 SQL 规一化后算出的唯一标识符。SQL 的规一化会忽略常量、空白符、大小写的差别。即语法一致的 SQL 语句,其 digest 也相同。 @@ -31,18 +38,16 @@ select * from employee where id in (...) and salary between ? and ?; 此处的 plan digest 是把执行计划规一化后算出的唯一标识符。执行计划的规一化会忽略常量的差别。由于相同的 SQL 可能产生不同的执行计划,所以可能分到多个组,同一个组内的执行计划是相同的。 -`events_statements_summary_by_digest` 用于保存 SQL 监控指标聚合后的结果。一般来说,每一项监控指标都包含平均值和最大值。例如执行延时对应 `AVG_LATENCY` 和 `MAX_LATENCY` 两个字段,分别是平均延时和最大延时。 - -为了监控指标的即时性,`events_statements_summary_by_digest` 里的数据定期被清空,只展现最近一段时间内的聚合结果。清空周期由系统变量 `tidb_stmt_summary_refresh_interval` 设置。如果刚好在清空之后进行查询,显示的数据可能很少。 +`statements_summary` 用于保存 SQL 监控指标聚合后的结果。一般来说,每一项监控指标都包含平均值和最大值。例如执行延时对应 `AVG_LATENCY` 和 `MAX_LATENCY` 两个字段,分别是平均延时和最大延时。 -因为 TiDB 中的很多概念不同于 MySQL,所以 TiDB 中 `events_statements_summary_by_digest` 的表结构与 MySQL 中的有很大区别。 +为了监控指标的即时性,`statements_summary` 里的数据定期被清空,只展现最近一段时间内的聚合结果。清空周期由系统变量 `tidb_stmt_summary_refresh_interval` 设置。如果刚好在清空之后进行查询,显示的数据可能很少。 -以下为查询 `events_statements_summary_by_digest` 的部分结果: +以下为查询 `statements_summary` 的部分结果: ``` SUMMARY_BEGIN_TIME: 2020-01-02 11:00:00 SUMMARY_END_TIME: 2020-01-02 11:30:00 - STMT_TYPE: select + STMT_TYPE: Select SCHEMA_NAME: test DIGEST: 0611cc2fe792f8c146cc97d39b31d9562014cf15f8d41f23a4938ca341f54182 DIGEST_TEXT: select * from employee where id = ? @@ -74,7 +79,109 @@ select * from employee where id in (...) and salary between ? and ?; > > 在 TiDB 中,statement summary tables 中字段的时间单位是纳秒 (ns),而 MySQL 中的时间单位是皮秒 (ps)。 -### 表的字段介绍 +## `statements_summary_history` + +`statements_summary_history` 的表结构与 `statements_summary` 完全相同,用于保存历史时间段的数据。通过历史数据,可以排查过去出现的异常,也可以对比不同时间的监控指标。 + +字段 `SUMMARY_BEGIN_TIME` 和 `SUMMARY_END_TIME` 代表历史时间段的开始时间和结束时间。 + +## `cluster_statements_summary` 和 `cluster_statements_summary_history` + +`statements_summary` 和 `statements_summary_history` 仅显示单台 TiDB server 的 statement summary 数据。要查询整个集群的数据,需要查询 `cluster_statements_summary` 和 `cluster_statements_summary_history`。 + +`cluster_statements_summary` 显示各台 TiDB server 的 `statements_summary` 数据,`cluster_statements_summary_history` 显示各台 TiDB server 的 `statements_summary_history` 数据。这两张表用字段 `INSTANCE` 表示 TiDB server 的地址,其他字段与 `statements_summary` 相同。 + +## 参数配置 + +以下系统变量用于控制 statement summary: + +- `tidb_enable_stmt_summary`:是否打开 statement summary 功能。1 代表打开,0 代表关闭,默认打开。statement summary 关闭后,系统表里的数据会被清空,下次打开后重新统计。经测试,打开后对性能几乎没有影响。 +- `tidb_stmt_summary_refresh_interval`:`statements_summary` 的清空周期,单位是秒 (s),默认值是 `1800`。 +- `tidb_stmt_summary_history_size`:`statements_summary_history` 保存每种 SQL 的历史的数量,默认值是 `24`。 +- `tidb_stmt_summary_max_stmt_count`:statement summary tables 保存的 SQL 种类数量,默认 200 条。当 SQL 种类超过该值时,会移除最近没有使用的 SQL。 +- `tidb_stmt_summary_max_sql_length`:字段 `DIGEST_TEXT` 和 `QUERY_SAMPLE_TEXT` 的最大显示长度,默认值是 4096。 +- `tidb_stmt_summary_internal_query`:是否统计 TiDB 的内部 SQL。1 代表统计,0 代表不统计,默认不统计。 + +statement summary 配置示例如下: + +```sql +set global tidb_enable_stmt_summary = true; +set global tidb_stmt_summary_refresh_interval = 1800; +set global tidb_stmt_summary_history_size = 24; +``` + +以上配置生效后,`statements_summary` 每 30 分钟清空一次。因为 24 * 30 分钟 = 12 小时,所以 `statements_summary_history` 保存最近 12 小时的历史数据。 + +以上几个系统变量都有 global 和 session 两种作用域,它们的生效方式与其他系统变量不一样: + +- 设置 global 变量后整个集群立即生效 +- 设置 session 变量后当前 TiDB server 立即生效,这对于调试单个 TiDB server 比较有用 +- 优先读 session 变量,没有设置过 session 变量才会读 global 变量 +- 把 session 变量设为空字符串,将会重新读 global 变量 + +> **注意:** +> +> `tidb_stmt_summary_history_size`、`tidb_stmt_summary_max_stmt_count`、`tidb_stmt_summary_max_sql_length` 这些配置都影响内存占用,建议根据实际情况调整,不宜设置得过大。 + +## 目前的限制 + +Statement summary tables 现在还存在以下限制: + +- TiDB server 重启后 statement summary 会丢失。因为 statement summary tables 是内存表,不会持久化数据,所以一旦 server 被重启,statement summary 随之丢失。 + +## 排查示例 + +下面用两个示例问题演示如何利用 statement summary 来排查。 + +### SQL 延迟比较大,是不是服务端的问题? + +例如客户端显示 employee 表的点查比较慢,那么可以按 SQL 文本来模糊查询: + +```sql +SELECT avg_latency, exec_count, query_sample_text + FROM information_schema.statements_summary + WHERE digest_text LIKE 'select * from employee%'; +``` + +结果如下,`avg_latency` 是 1 ms 和 0.3 ms,在正常范围,所以可以判定不是服务端的问题,继而排查客户端或网络问题。 + +``` ++-------------+------------+------------------------------------------+ +| avg_latency | exec_count | query_sample_text | ++-------------+------------+------------------------------------------+ +| 1042040 | 2 | select * from employee where name='eric' | +| 345053 | 3 | select * from employee where id=3100 | ++-------------+------------+------------------------------------------+ +2 rows in set (0.00 sec) +``` + +### 哪类 SQL 的总耗时最高? + +假如上午 10:00 到 10:30 的 QPS 明显下降,可以从历史表中找出当时耗时最高的三类 SQL: + +```sql +SELECT sum_latency, avg_latency, exec_count, query_sample_text + FROM information_schema.statements_summary_history + WHERE summary_begin_time='2020-01-02 10:00:00' + ORDER BY sum_latency DESC LIMIT 3; +``` + +结果显示以下三类 SQL 的总延迟最高,所以这些 SQL 需要重点优化。 + +``` ++-------------+-------------+------------+-----------------------------------------------------------------------+ +| sum_latency | avg_latency | exec_count | query_sample_text | ++-------------+-------------+------------+-----------------------------------------------------------------------+ +| 7855660 | 1122237 | 7 | select avg(salary) from employee where company_id=2013 | +| 7241960 | 1448392 | 5 | select * from employee join company on employee.company_id=company.id | +| 2084081 | 1042040 | 2 | select * from employee where name='eric' | ++-------------+-------------+------------+-----------------------------------------------------------------------+ +3 rows in set (0.00 sec) +``` + +## 表的字段介绍 + +下面介绍 `statements_summary` 表中各个字段的含义。 SQL 的基础信息: @@ -99,6 +206,8 @@ SQL 的基础信息: 在 TiDB server 上的执行数据: - `EXEC_COUNT`:这类 SQL 的总执行次数 +- `SUM_ERRORS`:执行过程中遇到的 error 的总数 +- `SUM_WARNINGS`:执行过程中遇到的 warning 的总数 - `SUM_LATENCY`:这类 SQL 的总延时 - `MAX_LATENCY`:这类 SQL 的最大延时 - `MIN_LATENCY`:这类 SQL 的最小延时 @@ -112,11 +221,9 @@ SQL 的基础信息: 和 TiKV Coprocessor Task 相关的字段: -- `COP_TASK_NUM`:每条 SQL 发送的 Coprocessor 请求数量 -- `AVG_COP_PROCESS_TIME`:cop-task 的平均处理时间 +- `SUM_COP_TASK_NUM`:发送 Coprocessor 请求的总数 - `MAX_COP_PROCESS_TIME`:cop-task 的最大处理时间 - `MAX_COP_PROCESS_ADDRESS`:执行时间最长的 cop-task 所在地址 -- `AVG_COP_WAIT_TIME`:cop-task 的平均等待时间 - `MAX_COP_WAIT_TIME`:cop-task 的最大等待时间 - `MAX_COP_WAIT_ADDRESS`:等待时间最长的 cop-task 所在地址 - `AVG_PROCESS_TIME`:SQL 在 TiKV 的平均处理时间 @@ -156,106 +263,3 @@ SQL 的基础信息: - `BACKOFF_TYPES`:遇到需要重试的错误时的所有错误类型及每种类型重试的次数,格式为 `类型:次数`。如有多种错误则用 `,` 分隔,例如 `txnLock:2,pdRPC:1` - `AVG_AFFECTED_ROWS`:平均影响行数 - `PREV_SAMPLE_TEXT`:当 SQL 是 `COMMIT` 时,该字段为 `COMMIT` 的前一条语句;否则该字段为空字符串。当 SQL 是 `COMMIT` 时,按 digest 和 `prev_sample_text` 一起分组,即不同 `prev_sample_text` 的 `COMMIT` 也会分到不同的行 - -## `events_statements_summary_by_digest_history` - -`events_statements_summary_by_digest_history` 的表结构与 `events_statements_summary_by_digest` 完全相同,用于保存历史时间段的数据。通过历史数据,可以排查过去出现的异常,也可以对比不同时间的监控指标。 - -字段 `SUMMARY_BEGIN_TIME` 和 `SUMMARY_END_TIME` 代表历史时间段的开始时间和结束时间。 - -## 排查示例 - -下面用两个示例问题演示如何利用 statement summary 来排查。 - -### SQL 延迟比较大,是不是服务端的问题? - -例如客户端显示 employee 表的点查比较慢,那么可以按 SQL 文本来模糊查询: - -```sql -SELECT avg_latency, exec_count, query_sample_text - FROM performance_schema.events_statements_summary_by_digest - WHERE digest_text LIKE 'select * from employee%'; -``` - -结果如下,`avg_latency` 是 1 ms 和 0.3 ms,在正常范围,所以可以判定不是服务端的问题,继而排查客户端或网络问题。 - -``` -+-------------+------------+------------------------------------------+ -| avg_latency | exec_count | query_sample_text | -+-------------+------------+------------------------------------------+ -| 1042040 | 2 | select * from employee where name='eric' | -| 345053 | 3 | select * from employee where id=3100 | -+-------------+------------+------------------------------------------+ -2 rows in set (0.00 sec) -``` - -### 哪类 SQL 的总耗时最高? - -假如上午 10:00 到 10:30 的 QPS 明显下降,可以从历史表中找出当时耗时最高的三类 SQL: - -```sql -SELECT sum_latency, avg_latency, exec_count, query_sample_text - FROM performance_schema.events_statements_summary_by_digest_history - WHERE summary_begin_time='2020-01-02 10:00:00' - ORDER BY sum_latency DESC LIMIT 3; -``` - -结果显示以下三类 SQL 的总延迟最高,所以这些 SQL 需要重点优化。 - -``` -+-------------+-------------+------------+-----------------------------------------------------------------------+ -| sum_latency | avg_latency | exec_count | query_sample_text | -+-------------+-------------+------------+-----------------------------------------------------------------------+ -| 7855660 | 1122237 | 7 | select avg(salary) from employee where company_id=2013 | -| 7241960 | 1448392 | 5 | select * from employee join company on employee.company_id=company.id | -| 2084081 | 1042040 | 2 | select * from employee where name='eric' | -+-------------+-------------+------------+-----------------------------------------------------------------------+ -3 rows in set (0.00 sec) -``` - -## 参数配置 - -statement summary 功能默认关闭,通过设置系统变量打开,例如: - -```sql -set global tidb_enable_stmt_summary = true; -``` - -statement summary 关闭后,系统表里的数据会被清空,下次打开后重新统计。经测试,打开后对性能几乎没有影响。 - -还有两个控制 statement summary 的系统变量: - -- `tidb_stmt_summary_refresh_interval`:`events_statements_summary_by_digest` 的清空周期,单位是秒 (s),默认值是 `1800`。 -- `tidb_stmt_summary_history_size`:`events_statements_summary_by_digest_history` 保存每种 SQL 的历史的数量,默认值是 `24`。 - -statement summary 配置示例如下: - -```sql -set global tidb_stmt_summary_refresh_interval = 1800; -set global tidb_stmt_summary_history_size = 24; -``` - -以上配置生效后,`events_statements_summary_by_digest` 每 30 分钟清空一次,`events_statements_summary_by_digest_history` 保存最近 12 小时的历史数据。 - -以上两个系统变量都有 global 和 session 两种作用域,它们的生效方式与其他系统变量不一样: - -- 设置 global 变量后整个集群立即生效 -- 设置 session 变量后当前 TiDB server 立即生效,这对于调试单个 TiDB server 比较有用 -- 优先读 session 变量,没有设置过 session 变量才会读 global 变量 -- 把 session 变量设为空字符串,将会重新读 global 变量 - -由于 statement summary tables 是内存表,为了防止内存问题,需要限制保存的 SQL 条数和 SQL 的最大显示长度。这两个参数都在 config.toml 的 `[stmt-summary]` 类别下配置: - -- 通过 `max-stmt-count` 更改保存的 SQL 种类数量,默认 200 条。当 SQL 种类超过 `max-stmt-count` 时,会移除最近没有使用的 SQL。 -- 通过 `max-sql-length` 更改 `DIGEST_TEXT` 和 `QUERY_SAMPLE_TEXT` 的最大显示长度,默认是 4096。 - -> **注意:** -> -> `tidb_stmt_summary_history_size`、`max-stmt-count`、`max-sql-length` 这些配置都影响内存占用,建议根据实际情况调整,不宜设置得过大。 - -## 目前的限制 - -Statement summary tables 现在还存在一些限制: - -- 查询 statement summary tables 时,只会显示当前 TiDB server 的 statement summary,而不是整个集群的 statement summary。 -- TiDB server 重启后 statement summary 会丢失。因为 statement summary tables 是内存表,不会持久化数据,所以一旦 server 被重启,statement summary 随之丢失。