MySQL導入數(shù)據(jù)后導致SQL性能下降


這個問題是線上遇到的問題,BUG有關(guān),遇到后可能導致SQL運行緩慢,導致大量的慢查詢,進而影響線上業(yè)務,還是需要警惕。

一、被關(guān)閉的自動統(tǒng)計數(shù)據(jù)收集

首先MySQL 8.0默認全部使用persist統(tǒng)計數(shù)據(jù)(innodb_stats_persistent=ON),統(tǒng)計數(shù)據(jù)放到mysql.innodb_table_stats和mysql.innodb_index_stats表中,內(nèi)存中使用table->stat_n_rows等信息表示,正常情況下是先改內(nèi)存,在寫入表中,重啟后會使用表的信息初始化內(nèi)存信息,
一般情況下是同步的。但是mysqldump可能導致信息不同步,并且損壞mysql.innodb_table_stats和mysql.innodb_index_stats表中的信息。
一般情況下,如果修改量超過當前統(tǒng)計數(shù)據(jù)行數(shù)的1/10,則需要收集統(tǒng)計數(shù)據(jù),會推給后臺收集線程進行統(tǒng)計數(shù)據(jù)收集。
但是在以下的情況下會使用/*!50606 SET GLOBAL INNODB_STATS_AUTO_RECALC=OFF */ 關(guān)閉自動統(tǒng)計數(shù)據(jù)的收集,也就是導入的時候插入數(shù)據(jù)不觸發(fā)自動統(tǒng)計數(shù)據(jù)收集,

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

這個函數(shù)為is_innodb_stats_tables_included,定義了這種行為,并且用trae分析了一下也得到了同樣的結(jié)果,如下,

image.png

image.png

而就是這個原因?qū)е铝私y(tǒng)計數(shù)據(jù)的丟失,我們繼續(xù)分析。

二、統(tǒng)計信息丟失

這里存在2個問題點如下,包含統(tǒng)計數(shù)據(jù)的內(nèi)存信息和持久化表,

1、 雖然導入的時候會導入innodb_table_stats表,但是語句執(zhí)行的時候使用的是table->stat_n_rows的內(nèi)存信息,因為前面說的自動統(tǒng)計數(shù)據(jù)收集已經(jīng)關(guān)閉,因此無法得到更改,

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

2、雖然導入的時候會導入innodb_table_stats表,然后執(zhí)行drop table/create table,接著insert 數(shù)據(jù),但是持久化的信息也會被create table 覆蓋掉innodb_table_stats中的信息,因此innodb_table_stats表中的統(tǒng)計信息會為0,而insert 數(shù)據(jù)由于關(guān)閉了自動統(tǒng)計數(shù)據(jù)收集,因此不會自動收集統(tǒng)計數(shù)據(jù),這樣導致數(shù)據(jù)導入完成后innodb_table_stats中大量的0,也就是沒有統(tǒng)計信息。

三、測試

這里使用8023進行測試,同時測試8041都是一樣的結(jié)果,注意持久化統(tǒng)計數(shù)據(jù)中mytest的統(tǒng)計信息。

導入前

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 |
...
+---------------+---------------+---------------------+--------+----------------------+--------------------------+

統(tǒng)計數(shù)據(jù)內(nèi)存和表同步


導入后,統(tǒng)計數(shù)據(jù)內(nèi)存和表都是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來導出數(shù)據(jù),導出需要的數(shù)據(jù)庫即可,避免這種情況。

  • B:導入數(shù)據(jù)后新庫檢查mysql.innodb_table_stats和mysql.innodb_index_stats表,如果沒有大量的0,可以考慮重啟一次數(shù)據(jù)庫,啟動后檢查自動收集統(tǒng)計信息的參數(shù)innodb_stats_auto_recalc是否為ON。

  • C:如果出現(xiàn)類似問題倒完數(shù)據(jù)后,盡快全庫收集統(tǒng)計數(shù)據(jù),使用analyze table 即可。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容