MySQL导入数据后导致SQL性能下降


这个问题是线上遇到的问题,BUG有关,遇到后可能导致SQL运行缓慢,导致大量的慢查询,进而影响线上业务,还是需要警惕。

一、被关闭的自动统计数据收集

首先MySQL 8.0默认全部使用persist统计数据(innodb_stats_persistent=ON),统计数据放到mysql.innodb_table_stats和mysql.innodb_index_stats表中,内存中使用table->stat_n_rows等信息表示,正常情况下是先改内存,在写入表中,重启后会使用表的信息初始化内存信息,
一般情况下是同步的。但是mysqldump可能导致信息不同步,并且损坏mysql.innodb_table_stats和mysql.innodb_index_stats表中的信息。
一般情况下,如果修改量超过当前统计数据行数的1/10,则需要收集统计数据,会推给后台收集线程进行统计数据收集。
但是在以下的情况下会使用/*!50606 SET GLOBAL INNODB_STATS_AUTO_RECALC=OFF */ 关闭自动统计数据的收集,也就是导入的时候插入数据不触发自动统计数据收集,

  • 导出database包含mysql库
  • 导出使用--all-databases(主要)
  • 导出包含mysql.innodb_table_stats和mysql.innodb_index_stats表

这个函数为is_innodb_stats_tables_included,定义了这种行为,并且用trae分析了一下也得到了同样的结果,如下,

image.png

image.png

而就是这个原因导致了统计数据的丢失,我们继续分析。

二、统计信息丢失

这里存在2个问题点如下,包含统计数据的内存信息和持久化表,

1、 虽然导入的时候会导入innodb_table_stats表,但是语句执行的时候使用的是table->stat_n_rows的内存信息,因为前面说的自动统计数据收集已经关闭,因此无法得到更改,

(gdb) p prebuilt->table->stat_n_rows
$4 = 0

2、虽然导入的时候会导入innodb_table_stats表,然后执行drop table/create table,接着insert 数据,但是持久化的信息也会被create table 覆盖掉innodb_table_stats中的信息,因此innodb_table_stats表中的统计信息会为0,而insert 数据由于关闭了自动统计数据收集,因此不会自动收集统计数据,这样导致数据导入完成后innodb_table_stats中大量的0,也就是没有统计信息。

三、测试

这里使用8023进行测试,同时测试8041都是一样的结果,注意持久化统计数据中mytest的统计信息。

导入前

mysql> desc select count(*) from mytest ;
+----+-------------+--------+------------+-------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table  | partitions | type  | possible_keys | key  | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+--------+------------+-------+---------------+------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | mytest | NULL       | index | NULL          | id   | 5       | NULL | 65920 |   100.00 | Using index |
+----+-------------+--------+------------+-------+---------------+------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

(gdb) p prebuilt->table->stat_n_rows
$3 = 65920

mysql> select * from mysql.innodb_table_stats;
+---------------+---------------+---------------------+--------+----------------------+--------------------------+
| database_name | table_name    | last_update         | n_rows | clustered_index_size | sum_of_other_index_sizes |
+---------------+---------------+---------------------+--------+----------------------+--------------------------+
...
| stattest      | mytest        | 2025-06-06 15:22:09 |  65920 |                  161 |                       97 |
...
+---------------+---------------+---------------------+--------+----------------------+--------------------------+

统计数据内存和表同步


导入后,统计数据内存和表都是0
mysql> desc select count(*) from mytest ;
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | mytest | NULL       | index | NULL          | id   | 5       | NULL |    1 |   100.00 | Using index |
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

(gdb) p prebuilt->table->stat_n_rows
$4 = 0


mysql> select * from mysql.innodb_table_stats;
+---------------+---------------+---------------------+--------+----------------------+--------------------------+
| database_name | table_name    | last_update         | n_rows | clustered_index_size | sum_of_other_index_sizes |
+---------------+---------------+---------------------+--------+----------------------+--------------------------+
...
| stattest      | mytest        | 2025-06-06 07:00:06 |      0 |                    1 |                        1 |      
...

四、bug和建议

从影响范围来看,影响还是比较广,因此需要注意一下,bug如下,

  • https://bugs.mysql.com/bug.php?id=98178
    5.6 5.7 8.0 都存在问题,建议如下,

  • A:避免使用--all-databases来导出数据,导出需要的数据库即可,避免这种情况。

  • B:导入数据后新库检查mysql.innodb_table_stats和mysql.innodb_index_stats表,如果没有大量的0,可以考虑重启一次数据库,启动后检查自动收集统计信息的参数innodb_stats_auto_recalc是否为ON。

  • C:如果出现类似问题倒完数据后,尽快全库收集统计数据,使用analyze table 即可。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容