Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reference: update statement summary tables #3145

Merged
merged 7 commits into from
May 25, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
236 changes: 120 additions & 116 deletions statement-summary-tables.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 也相同。

Expand All @@ -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 = ?
Expand Down Expand Up @@ -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 的基础信息:

Expand All @@ -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 的最小延时
Expand All @@ -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 的平均处理时间
Expand Down Expand Up @@ -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 随之丢失。