# 检查SQL语句

## **1. 启用插桩**

<mark style="color:orange;">**要启用语句检测，需要启用statement类型的插桩（默认已经启用）**</mark>

```sql
UPDATE setup_instruments
SET enabled = 'YES'
WHERE name LIKE 'statement/%'
  AND enabled = 'NO';
```

相关的插桩如下表：

<table data-view="cards"><thead><tr><th>插桩类</th><th>描述</th></tr></thead><tbody><tr><td><mark style="color:blue;"><strong>statement/sql</strong></mark></td><td>SQL语句，如 SELECT、CREATE TABLE等</td></tr><tr><td><mark style="color:blue;"><strong>statement/sp</strong></mark></td><td>存储过程控制</td></tr><tr><td><mark style="color:blue;"><strong>statement/scheduler</strong></mark></td><td>事件调度器</td></tr><tr><td><mark style="color:blue;"><strong>statement/com</strong></mark></td><td>命令，如 quit、kill、DROP DATABASE、Binlog Dump等<br>有些命令是用户不可用的，只能由 mysqld 进程调用</td></tr><tr><td><mark style="color:blue;"><strong>statement/abstract</strong></mark></td><td>包括 4 类命令：clone、Query、new_packet 和 relay_log</td></tr></tbody></table>

## 2. 启用消费者表

Performance Schema将语句指标存储在 <mark style="color:blue;">**events\_statements\_current**</mark>、<mark style="color:blue;">**events\_statements\_history**</mark> 和 <mark style="color:blue;">**events\_statements\_history\_long**</mark> 表中。因此，接着需要启用（enable）这三张消费者表：

```sql
UPDATE setup_consumers
SET enabled = 'YES'
WHERE name LIKE 'events_statements_%'
  AND enabled = 'NO';
```

{% hint style="info" %}
在5.7版本中， <mark style="color:blue;">**events\_statements\_current**</mark>、<mark style="color:blue;">**events\_statements\_history**</mark> 默认处于enabled状态，<mark style="color:blue;">**events\_statements\_history\_long**</mark> 则处于disabled状态。
{% endhint %}

## 3. 查询消费者表，找出需要优化的SQL语句

在 <mark style="color:blue;">**events\_statements\_history**</mark> 表中可以作为优化指标的列如下：

| 列                                                               | 描述                                                                                       | 重要性                                      |
| --------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------- |
| <mark style="color:blue;">**CREATED\_TMP\_DISK\_TABLES**</mark> | 查询创建的临时磁盘表的数量。                                                                           | <mark style="color:red;">**高**</mark>    |
| <mark style="color:blue;">**CREATED\_TMP\_TABLES**</mark>       | <p>查询创建的临时表的数量。<br></p>                                                                  | <mark style="color:orange;">**中**</mark> |
| <mark style="color:blue;">**SELECT\_FULL\_JOIN**</mark>         | 因为没有合适的索引，所以导致JOIN执行了全表扫描。                                                               | <mark style="color:red;">**高**</mark>    |
| <mark style="color:blue;">**SELECT\_FULL\_RANGE\_JOIN**</mark>  | JOIN操作是否使用了被引用表的范围搜索。                                                                    | <mark style="color:orange;">**中**</mark> |
| <mark style="color:blue;">**SELECT\_RANGE**</mark>              | JOIN操作是否使用了范围搜索来解析第一个表中的行。                                                               | 低                                        |
| <mark style="color:blue;">**SELECT\_RANGE\_CHECK**</mark>       | <p>如果JOIN操作没有索引，则会检查每一行之后的键。<br><strong>这是一种非常糟糕的症状，如果该值大于零，则需要重新考虑表中的索引设计。</strong></p> | <mark style="color:red;">**高**</mark>    |
| <mark style="color:blue;">**SELECT\_SCAN**</mark>               | <p>JOIN操作是否对第一个表执行了全表扫描。<br><strong>如果第一个表很大则会使一个问题。</strong></p>                        | <mark style="color:orange;">**中**</mark> |
| <mark style="color:blue;">**SORT\_MERGE\_PASSES**</mark>        | <p>排序必须执行的合并过程数。<br><strong>如果该值大于零且查询性能较低，则可能需要增加 sort\_buffer\_size 的值。</strong></p>   | 低                                        |
| <mark style="color:blue;">**SORT\_RANGE**</mark>                | 是否使用的是范围排序。                                                                              | 低                                        |
| <mark style="color:blue;">**SORT\_ROWS**</mark>                 | <p>排序的行数。<br><strong>如果排序的行数比返回的行数多，则可能需要优化查询。</strong></p>                              | <mark style="color:orange;">**中**</mark> |
| <mark style="color:blue;">**SORT\_SCAN**</mark>                 | <p>排序是否是通过扫描表完成的。<br><strong>这是一个非常糟糕的迹象，除非有意选择表中的所有行而不使用索引。</strong></p>                | <mark style="color:red;">**高**</mark>    |
| <mark style="color:blue;">**NO\_INDEX\_USED**</mark>            | 查询没有使用索引。                                                                                | <mark style="color:red;">**高**</mark>    |
| <mark style="color:blue;">**NO\_GOOD\_INDEX\_USED**</mark>      | <p>查询所用的索引不是最合适的。<br><strong>如果该值大于零，则需要重新考虑索引的设计。</strong></p>                          | <mark style="color:red;">**高**</mark>    |

要找出哪些语句需要优化，可以选择上述列中的任何一列，并将其与 0 进行比较。

### 找到所有没有使用合适索引的查询

```sql
SELECT
    thread_id,
    sql_text,
    rows_sent,
    rows_examined,
    created_tmp_disk_tables,
    created_tmp_tables,
    no_index_used,
    no_good_index_used
FROM events_statements_history
WHERE no_index_used > 0
   OR no_good_index_used > 0;
```

### 找出所有创建了临时表的查询

```sql
SELECT
    thread_id,
    sql_text,
    rows_sent,
    rows_examined,
    created_tmp_disk_tables,
    created_tmp_tables,
    no_index_used,
    no_good_index_used
FROM events_statements_history
WHERE created_tmp_disk_tables > 0
   OR created_tmp_tables > 0;
```

### 找出所有返回错误的查询

```sql
SELECT
    thread_id,
    sql_text,
    mysql_errno,
    returned_sqlstate,
    message_text,
    errors
FROM events_statements_history
WHERE errors > 0;
```

### 找出所有执行时间超过 5 秒的查询

```sql
SELECT
    *
FROM events_statements_history
WHERE timer_wait > 5000000000;
```

## 4. 使用sys Schema

sys schema提供了可用于查找有问题语句的视图，如下表所示：

<table><thead><tr><th width="329">视图</th><th>描述</th></tr></thead><tbody><tr><td><mark style="color:blue;"><strong>statement_analysis</strong></mark></td><td><p>具有聚合统计信息的规范化语句视图，按每个规范化语句的总执行时间进行排序。</p><p>类似于 events_statements_summary_by_digest表，但没有那么详细。</p></td></tr><tr><td><mark style="color:blue;"><strong>statements_with_errors_or_warnings</strong></mark> </td><td>所有引起错误或警告的规范化语句。</td></tr><tr><td><mark style="color:blue;"><strong>statements_with_full_table_scans</strong></mark> </td><td>所有执行了全表扫描的规范化语句。</td></tr><tr><td><mark style="color:blue;"><strong>statements_with_runtimes_in_95th_percentile</strong></mark></td><td>所有平均执行时间在前 95% 的规范化语句。</td></tr><tr><td><mark style="color:blue;"><strong>statements_with_sorting</strong></mark></td><td>所有执行了排序的规范化语句。</td></tr><tr><td><mark style="color:blue;"><strong>statements_with_temp_tables</strong></mark></td><td>所有使用了临时表的规范化语句。</td></tr></tbody></table>

sys schema 使用<mark style="color:blue;">**摘要文本**</mark>而不是查询文本，因此你将获得查询文本的摘要，而不是像直接访问Performance Schema那样获得的要么是SQL语句要么是摘要文本：

```sql
SELECT
    query
FROM sys.statements_with_temp_tables
WHERE db = 'sakila'
  AND query LIKE '% ORDER BY %';
  
+-----------------------------------------------------------------+
|query                                                            |
+-----------------------------------------------------------------+
|EXPLAIN SELECT `payment_date`  ...  BY `payment_date` ORDER BY ? |
+-----------------------------------------------------------------+
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://bohans.gitbook.io/ji-chu/mysql/sql-you-hua/performance-schema/shi-yong/jian-cha-sql-yu-ju.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
