Bug #94383 | simple ALTER cause unnecessary InnoDB index rebuilds, 5.7.23 or later 5.7 rlses | ||
---|---|---|---|
Submitted: | 18 Feb 2019 15:49 | Modified: | 1 Apr 2019 17:49 |
Reporter: | Mikhail Izioumtchenko (OCA) | Email Updates: | |
Status: | Closed | Impact on me: | |
Category: | MySQL Server: InnoDB storage engine | Severity: | S3 (Non-critical) |
Version: | 5.7.23, 5.7.25 | OS: | Any |
Assigned to: | CPU Architecture: | Any |
[18 Feb 2019 15:49]
Mikhail Izioumtchenko
[18 Feb 2019 16:09]
Mikhail Izioumtchenko
to reproduce: first, we need to create a table on 5.7.22: create table test_varchar(i int primary key, v varchar(40), key (v)); then we upgrade to 5.7.23 or later. In this example I'm using 5.7.25. We need a diagnostics patch that I will post in the next comment. In particular it asserts in the routine that InnoDB uses to rebuild an index: diff -u -r mysql-5.7.25-orig/storage/innobase/row/row0merge.cc mysql-5.7.25/storage/innobase/row/row0merge.cc --- mysql-5.7.25-orig/storage/innobase/row/row0merge.cc>2019-02-17 08:17:13.399279574 +0100 +++ mysql-5.7.25/storage/innobase/row/row0merge.cc>·2019-02-18 13:11:36.937551594 +0100 @@ -4430,6 +4433,8 @@ · >··/* Read clustered index of the table and create files for >··secondary index entries for merge sort */ +printf("#debug 25 finds %lu indexes\n", n_indexes); +ut_a(!"BUG25"); //#debug BUG25 >··error = row_merge_read_clustered_index( >··>···trx, table, old_table, new_table, online, indexes, >··>···fts_sort_idx, psort_info, merge_files, key_numbers, Then we try to run an ALTER: ... [test]> alter table test_varchar comment 'test-5.7.25'; ERROR 2013 (HY000): Lost connection to MySQL server during query and indeed InnoDB: Failing assertion: !"BUG25" The bug is that for ALTER TABLE ... COMMENT '...' there is no need to call row_merge_read_clustered_index(), and this is not happening in MySQL 5.7.22
[18 Feb 2019 16:10]
Mikhail Izioumtchenko
Diagnostic patches: patch for 5.7.22 I used to prove the keys are not rebuilt in 5.7.22: diff -u -r mysql-5.7.22-orig/storage/innobase/row/row0merge.cc mysql-5.7.22/storage/innobase/row/row0merge.cc --- mysql-5.7.22-orig/storage/innobase/row/row0merge.cc 2019-02-17 08:17:36.193419315 +0100 +++ mysql-5.7.22/storage/innobase/row/row0merge.cc 2019-02-18 12:25:45.249163115 +0100 @@ -4424,6 +4424,7 @@ /* Read clustered index of the table and create files for secondary index entries for merge sort */ +ut_a(!"BUG25"); //#debug BUG25 error = row_merge_read_clustered_index( trx, table, old_table, new_table, online, indexes, fts_sort_idx, psort_info, merge_files, key_numbers,
[18 Feb 2019 16:12]
Mikhail Izioumtchenko
and complete giagnostics patch for 5.7.25: diff -u -r mysql-5.7.25-orig/sql/sql_table.cc mysql-5.7.25/sql/sql_table.cc --- mysql-5.7.25-orig/sql/sql_table.cc 2019-02-17 08:17:13.373279414 +0100 +++ mysql-5.7.25/sql/sql_table.cc 2019-02-18 13:12:41.839963667 +0100 @@ -6278,8 +6278,13 @@ if ((table_key->algorithm != new_key->algorithm) || ((table_key->flags & HA_KEYFLAG_MASK) != (new_key->flags & HA_KEYFLAG_MASK)) || - (table_key->user_defined_key_parts != new_key->user_defined_key_parts)) + (table_key->user_defined_key_parts != new_key->user_defined_key_parts)) { +printf("#debug 25 in has_index_def_changed 1 ret True for %s\n",table_key->name); +printf("#debug 25 in has_index_def_changed alg %d vs %d\n", table_key->algorithm, new_key->algorithm); +printf("#debug 25 in has_index_def_changed parts %u vs %u\n", table_key->user_defined_key_parts, new_key->user_defined_key_parts); +printf("#debug 25 in has_index_def_changed flags %lu vs %lu and mask %u\n", (table_key->flags & HA_KEYFLAG_MASK), (new_key->flags & HA_KEYFLAG_MASK), HA_KEYFLAG_MASK); return true; +} /* If an index comment is added/dropped/changed, then mark it for a @@ -6815,6 +6820,7 @@ else if (has_index_def_changed(ha_alter_info, table_key, new_key)) { /* Key was modified. */ +printf("#debug 25 in fill_alter_inplace_info need modify key %s to %s\n",table_key->name,new_key->name); ha_alter_info->add_modified_key(table_key, new_key); } } @@ -6886,12 +6892,14 @@ } else { +printf("#debug 25 fill_alter_inplace_info drop unique index %s BUG\n",table_key->name); ha_alter_info->handler_flags|= Alter_inplace_info::DROP_UNIQUE_INDEX; if (is_candidate_key(table_key)) candidate_key_count--; } } else +printf("#debug 25 drop fill_alter_inplace_info non unique %s index BUG\n",table_key->name); ha_alter_info->handler_flags|= Alter_inplace_info::DROP_INDEX; } diff -u -r mysql-5.7.25-orig/storage/innobase/handler/handler0alter.cc mysql-5.7.25/storage/innobase/handler/handler0alter.cc --- mysql-5.7.25-orig/storage/innobase/handler/handler0alter.cc 2019-02-17 08:17:13.386279494 +0100 +++ mysql-5.7.25/storage/innobase/handler/handler0alter.cc 2019-02-18 13:11:58.583689017 +0100 @@ -6230,8 +6230,13 @@ DEBUG_SYNC(m_user_thd, "innodb_inplace_alter_table_enter"); +printf("#debug BUG25-enter-ha_innobase::inplace_alter_table\n");fflush(stdout); + +printf("#debug BUG25-p0-ha_innobase::inplace_alter_table flags %llu and %llu, expect not\n",ha_alter_info->handler_flags,INNOBASE_ALTER_DATA);fflush(stdout); if (!(ha_alter_info->handler_flags & INNOBASE_ALTER_DATA)) { +printf("#debug BUG25-p1-ha_innobase::inplace_alter_table\n");fflush(stdout); ok_exit: +printf("#debug BUG25-ok-exit-ha_innobase::inplace_alter_table\n");fflush(stdout); DEBUG_SYNC(m_user_thd, "innodb_after_inplace_alter_table"); DBUG_RETURN(false); } diff -u -r mysql-5.7.25-orig/storage/innobase/row/row0merge.cc mysql-5.7.25/storage/innobase/row/row0merge.cc --- mysql-5.7.25-orig/storage/innobase/row/row0merge.cc 2019-02-17 08:17:13.399279574 +0100 +++ mysql-5.7.25/storage/innobase/row/row0merge.cc 2019-02-18 13:11:36.937551594 +0100 @@ -4334,6 +4334,7 @@ int64_t sig_count = 0; bool fts_psort_initiated = false; DBUG_ENTER("row_merge_build_indexes"); +printf("BUG25-enter-row_merge_build_indexes\n");//#debug ut_ad(!srv_read_only_mode); ut_ad((old_table == new_table) == !col_map); @@ -4395,6 +4396,8 @@ merge_files[i].fd = -1; } +printf("#debug 25 n_indexes is now %lu\n",n_indexes); + for (i = 0; i < n_indexes; i++) { if (indexes[i]->type & DICT_FTS) { ibool opt_doc_id_size = FALSE; @@ -4430,6 +4433,8 @@ /* Read clustered index of the table and create files for secondary index entries for merge sort */ +printf("#debug 25 finds %lu indexes\n", n_indexes); +ut_a(!"BUG25"); //#debug BUG25 error = row_merge_read_clustered_index( trx, table, old_table, new_table, online, indexes, fts_sort_idx, psort_info, merge_files, key_numbers,
[18 Feb 2019 16:19]
Mikhail Izioumtchenko
what is happening: consider what we see in the 5.7.25 log after running alter table test_varchar_prefix comment 'test-5.7.25'; with the diagnostics patch above #debug 25 in has_index_def_changed 1 ret True for v #debug 25 in has_index_def_changed alg 0 vs 0 #debug 25 in has_index_def_changed parts 1 vs 1 #debug 25 in has_index_def_changed flags 32 vs 0 and mask 11699 #debug 25 in fill_alter_inplace_info need modify key v to v #debug 25 drop fill_alter_inplace_info non unique v index BUG #debug BUG25-enter-ha_innobase::inplace_alter_table #debug BUG25-p0-ha_innobase::inplace_alter_table flags 16777219 and 412334097589, expect not InnoDB: Failing assertion: !"BUG25" BUG25-enter-row_merge_build_indexes #debug 25 n_indexes is now 2 #debug 25 finds 2 indexes the root of the problem is this: #debug 25 in has_index_def_changed flags 32 vs 0 and mask 11699 so MySQL believes the BEFORE version of the key has a flag of 32 which is HA_BINARY_PACK_KEY, and it makes the decision to rebuild the index. I believe anything PACK related is not relevant to InnoDB so there is no need whatsoever to rebuild any index. One possible fix is to remove HA_BINARY_PACK_KEY from the definition of HA_KEYFLAG_MASK in include/my_base.h. I'm sure it will break at least MyISAM tests so the real fix will be slightly more complicated.
[18 Feb 2019 16:27]
Mikhail Izioumtchenko
Final observations: * the bug applies to all ALTER's that are nearly instantaneous in 5.7.22: column level comments, column defaults etc. * the bug is a pain when the table is big * it is especially unpleasant when the replication master is on 5.7.22 or lower and the slaves are on 5.7.23 or higher, then a fast ALTER on master may result in multihour replication delay * upgrade scripts and CHECK ... FOR UPGRADE do not catch this * the bug applies also to prefix indexes on VARCHAR's * it also applies to multicolumn indexes as long as there is a VARCHAR column * after one index rebuild, the table is 'fixed' so subsequent ALTER's of the kind do not cause the rebuild A candidate for the culprit (just a guess), a quote from 5.7.23 release notes: attempts to increase the length of a VARCHAR column of an InnoDB table using ALTER TABLE with the INPLACE algorithm, the attempt failed if the column was indexed. If an index size exceeded the InnoDB limit of 767 bytes for COMPACT or REDUNDANT row format, CREATE TABLE and ALTER TABLE did not report an error (in strict SQL mode) or a warning (in nonstrict mode). (Bug #26848813)
[19 Feb 2019 11:59]
MySQL Verification Team
Hello Mikhail, Thank you for the report. regards, Umesh
[19 Feb 2019 12:00]
MySQL Verification Team
test results
Attachment: 94383_5722_5725.results (application/octet-stream, text), 53.57 KiB.
[1 Apr 2019 17:49]
Paul DuBois
Posted by developer: Fixed in 5.7.27, 8.0.17. For InnoDB tables that contained an index on a VARCHAR column and were created prior to MySQL 5.7.23, some simple ALTER TABLE statements that should have been done in place were performed with a table rebuild after an upgrade to MySQL 5.7.23 or higher.
[21 May 2019 17:33]
Paul DuBois
Posted by developer: Correction: Bug occurs in 5.7 only, so it is fixed in 5.7.27 only, not 8.0.17.