有时,需要使用ANALYZE table命令手动更新表和索引统计信息。在不深入讨论这种需求的原因的情况下,我想从与在生产系统上运行该命令相关的开销方面刷新这个主题。然而,这里讨论的开销与深入表行收集统计信息的通常成本无关,我们可以通过设置示例页面的数量来控制统计信息。
五年前,Percona Server for MySQL中引入的一项改进,以解决与运行命令相关的不必要的停顿:
https://www.percona.com/blog/analyze-table-is-no-longer-a-blocking-operation/
从历史上看,在MySQL中运行ANALYZE TABLE命令的问题是查询需要对表的表定义缓存项进行独占锁定。这使得查询等待任何长时间运行的查询完成,但也可能触发级联等待其他传入请求。简而言之,ANALYZE可能会在繁忙的生产环境中导致严重的停机。
从那时起,情况发生了很大变化,但今天仍有许多生产系统运行受影响的版本。让我们回顾一下这些年来情况是如何演变的。
MySQL Server – Community Edition
该问题适用于8.0.23之前的所有上游MySQL社区版本。5.7系列没有任何改进(顺便说一句,十月将达到EOL!),这意味着即使是最新的5.7.43也会受到影响。以下是一个您可能会在此处结束的示例场景:
<code style="text-align: left;">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版本中有一个修复程序可以解决这个问题。我们只能在发布说明中读到关于“取消等待”的一些克制的评论:
https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-24.html
事实上,从版本8.0.24开始,长时间运行的查询中的类似测试会导致即时查询执行:
<code >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版本,如下所示:
请求更新相关的错误报告以及相应的文档问题:
https://bugs.mysql.com/bug.php?id=87065
https://bugs.mysql.com/bug.php?id=112670
Percona Server for MySQL
如上所述,作为解决该错误报告的结果,Percona引入了一个修复并删除了不必要的表定义缓存锁:
https://jira.percona.com/browse/PS-2503
当使用Percona变体时,从版本5.6.38和5.7.20开始运行ANALYZE TABLE就已经是安全的了,因为这些版本是当时的活动开发系列。您可以在此处阅读发行说明中的公告:
https://docs.percona.com/percona-server/5.7/release-notes/Percona-Server-5.7.20-18.html#bugs-fixed
Percona Server for MySQL 5.7从第一个版本(我测试过,包括第一个GA版本8.0.13-3)开始就没有这个问题,因为改进是从Percona Server for MySQL 5.7系列合并而来的。
总结
MySQL 8.0.24+版本最新版本上,运行ANALYZE TABLE应该是绝对安全的,不会导致任何意外的停顿。5.7系列没有任何改进,这意味着即使是最新的5.7.43也可能会受到影响。
Percona MySQL系列的三个服务器(5.6.38+、5.7.20+和8.0.x)的用户都是安全的,不会导致任何意外的停顿。