有时,需要运用 ANALYZE TABLE 指令手动更新表和索引计算信息。在不进一步探讨这种需求的原因的状况下,我想就与在出产体系上运转指令相关的开销来聊聊这个话题。然而,这里评论的开销与深入表行搜集计算信息的一般成本无关,咱们能够经过设置样本页数 来操控。

五年前,我的同事 Sveta 发布了一篇不错的博客文章,介绍了 Percona Server for MySQL 中引进的一项改进,以解一些不必要的等候:

《ANALYZE TABLE 不再是阻塞操作》

从历史上看,在 MySQL 中运转 ANALYZE TABLE 指令的问题是查询需要在表的表界说缓存条目上运用排他锁。这使得查询等候任何长期运转的查询完成,但也可能触发级联等候其他传入请求。简而言之,ANALYZE 可能会导致高负载出产环境中呈现较高得延时。

从那时起,MySQL/Percona/MariaDB 都发生了一些变化,但今天仍然存在许多出产体系会受影响的版别。让咱们回顾一下这些年来状况的演化。

MySQL

该问题适用于 MySQL 8.0.23 之前的所有版别。5.7 系列没有任何改进(顺便说一句,本月将到达 EOL!),这意味着即使是最新的 5.7.43 也会受到影响。以下是您可能会遇到的示例场景:

mysql > select @@version,@@version_comment;
 ----------- ------------------------------ 
| @@version | @@version_comment      |
 ----------- ------------------------------ 
| 5.7.43  | MySQL Community Server (GPL) |
 ----------- ------------------------------ 
1 row in set (0.00 sec)
mysql > show processlist;
 ---- ---------- ----------- ------ --------- ------ ------------------------- ---------------------------------------------------------------- 
| Id | User   | Host   | db  | Command | Time | State          | Info                              |
 ---- ---------- ----------- ------ --------- ------ ------------------------- ---------------------------------------------------------------- 
| 4 | msandbox | localhost | db1 | Query  |  54 | Sending data      | select avg(k) from sbtest1 where pad not like '%f%' group by c |
| 13 | msandbox | localhost | db1 | Query  |  29 | Waiting for table flush | analyze table sbtest1                     |
| 17 | msandbox | localhost | db1 | Query  |  0 | starting        | show processlist                        |
| 18 | msandbox | localhost | db1 | Query  |  15 | Waiting for table flush | select * from sbtest1 where id=100               |
 ---- ---------- ----------- ------ --------- ------ ------------------------- ---------------------------------------------------------------- 
4 rows in set (0.00 sec)

一个慢查询导致 ANALYZE 在等候,且另一个一般非常快的查询现在也在等候。

相同的状况也可能发生在 MySQL 8.0 系列,包含 8.0.23。走运的是,8.0.24 版别中修正了这个问题。咱们只能在发行阐明 中读到一些关于此问题处理得评论:

事实上,从版别 8.0.24 开端,运转慢查询期间的相似测验会导致即时查询执行:

mysql > select @@version,@@version_comment;
 ----------- ------------------------------ 
| @@version | @@version_comment      |
 ----------- ------------------------------ 
| 8.0.24  | MySQL Community Server - GPL |
 ----------- ------------------------------ 
1 row in set (0.00 sec)
mysql > analyze table sbtest1;
 ------------- --------- ---------- ---------- 
| Table    | Op   | Msg_type | Msg_text |
 ------------- --------- ---------- ---------- 
| db1.sbtest1 | analyze | status  | OK    |
 ------------- --------- ---------- ---------- 
1 row in set (0.00 sec)

不过,即使是 8.1 版别,咱们仍然能够在官方文档中找到警告,如下所示:

ANALYZE TABLE removes the table from the table definition cache, which requires a flush lock. If there are long running statements or transactions still using the table, subsequent statements and transactions must wait for those operations to finish before the flush lock is released. Because ANALYZE TABLE itself typically finishes quickly, it may not be apparent that delayed transactions or statements involving the same table are due to the remaining flush lock.

Percona

如上所述,为了处理此错误报告,Percona 引进了修正程序并删除了不必要的表界说缓存锁。

运用 Percona 时,从版别 5.6.38 和 5.7.20 开端运转 ANALYZE TABLE 已经是安全的,因为这些是其时活跃的版别。

第一个版别(包含第一个 GA 版别 8.0.13-3 )以来,Percona Server for MySQL 版别 8.0 就没有这个问题,因为改进是从 Percona Server for MySQL 5.7 系列合并而来的。

MariaDB

ANALYZE TABLE 的问题发生于 10.5.3 之前的所有 MariaDB 版别。在版别 10.5.4 中,Percona 得以处理。

因此,当您在 10.5.3 或更低版别以及任何以前的系列(乃至是最新的 10.4.31)中运转查询时,可能会呈现相似的状况:

mysql > select @@version,@@version_comment;
 ---------------- ------------------- 
| @@version   | @@version_comment |
 ---------------- ------------------- 
| 10.5.3-MariaDB | MariaDB Server  |
 ---------------- ------------------- 
1 row in set (0.000 sec)
mysql > show processlist;
 ---- ---------- ----------- ------ --------- ------ ------------------------- ---------------------------------------------------------------- ---------- 
| Id | User   | Host   | db  | Command | Time | State          | Info                              | Progress |
 ---- ---------- ----------- ------ --------- ------ ------------------------- ---------------------------------------------------------------- ---------- 
| 4 | msandbox | localhost | db1 | Query  |  18 | Sending data      | select avg(k) from sbtest1 where pad not like '%f%' group by c |  0.000 |
| 13 | msandbox | localhost | db1 | Query  |  16 | Waiting for table flush | analyze table sbtest1                     |  0.000 |
| 14 | msandbox | localhost | db1 | Query  |  14 | Waiting for table flush | select * from sbtest1 where id=100               |  0.000 |
| 15 | msandbox | localhost | NULL | Query  |  0 | starting        | show processlist                        |  0.000 |
 ---- ---------- ----------- ------ --------- ------ ------------------------- ---------------------------------------------------------------- ---------- 
4 rows in set (0.000 sec)
mysql > select @@version,@@version_comment;
 ----------------- ------------------- 
| @@version    | @@version_comment |
 ----------------- ------------------- 
| 10.4.31-MariaDB | MariaDB Server  |
 ----------------- ------------------- 
1 row in set (0.000 sec)
mysql > show processlist;
 ---- ------------- ----------- ------ --------- ------ -------------------------- ---------------------------------------------------------------- ---------- 
| Id | User    | Host   | db  | Command | Time | State          | Info                              | Progress |
 ---- ------------- ----------- ------ --------- ------ -------------------------- ---------------------------------------------------------------- ---------- 
| 1 | system user |      | NULL | Daemon | NULL | InnoDB purge coordinator | NULL                              |  0.000 |
| 2 | system user |      | NULL | Daemon | NULL | InnoDB purge worker   | NULL                              |  0.000 |
| 3 | system user |      | NULL | Daemon | NULL | InnoDB purge worker   | NULL                              |  0.000 |
| 4 | system user |      | NULL | Daemon | NULL | InnoDB purge worker   | NULL                              |  0.000 |
| 5 | system user |      | NULL | Daemon | NULL | InnoDB shutdown handler | NULL                              |  0.000 |
| 9 | msandbox  | localhost | db1 | Query  |  18 | Sending data       | select avg(k) from sbtest1 where pad not like '%f%' group by c |  0.000 |
| 18 | msandbox  | localhost | db1 | Query  |  16 | Waiting for table flush | analyze table sbtest1                     |  0.000 |
| 19 | msandbox  | localhost | db1 | Query  |  12 | Waiting for table flush | select * from sbtest1 where id=100               |  0.000 |
| 22 | msandbox  | localhost | NULL | Query  |  0 | Init           | show processlist                        |  0.000 |
 ---- ------------- ----------- ------ --------- ------ -------------------------- ---------------------------------------------------------------- ---------- 
9 rows in set (0.000 sec)

总结

只需您的数据库在最新版别的 MySQL 或 MariaDB 上运转,运转 ANALYZE TABLE 就应该绝对安全,不会导致任何意外的中止。

Percona Server for MySQL 系列:5.6.38 、5.7.20 和 8.0.x 的用户都是安全的。

MariaDB 用户必须升级到 10.5.4 或更高版别以防止确定问题。

更多技术文章,请访问:opensource.actionsky.com/

关于 SQLE

SQLE 是一款全方位的 SQL 质量管理渠道,覆盖开发至出产环境的 SQL 审核和管理。支撑主流的开源、商业、国产数据库,为开发和运维供给流程自动化才能,进步上线功率,进步数据质量。