Skip to content

Commit 3912660

Browse files
Luqun Louluqun
authored andcommitted
Don't update index stats during online ddl
Summary: This is a workaround for upstream bug https://bugs.mysql.com/bug.php?id=98132 The issue is that when online ddl is rebuilding table, if at same time, analyze table is running or auto recal is kicked in, index stats will be cleaned. before online ddl finished, optimzer may always pick full table over others index since optimizer think the table is empty. The change is to not empty index status if online ddl is running. also add a new varible innodb_stats_update_online_ddl to control whether update index stats when online ddl is running. Test Plan: CI Reviewers: mung, #mysql_eng Reviewed By: mung Subscribers: pgl, webscalesql-eng@fb.com Differential Revision: https://phabricator.intern.facebook.com/D40460252 Tasks: T134866239
1 parent b0a0b7c commit 3912660

File tree

8 files changed

+299
-4
lines changed

8 files changed

+299
-4
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#
2+
# update PK column
3+
#
4+
CREATE TABLE t1 (a int,
5+
b int,
6+
c int) ENGINE=InnoDB;
7+
insert into t1 values (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4);
8+
insert into t1 select a+4, b+4, c+4 from t1;
9+
insert into t1 select a+8, b+8, c+8 from t1;
10+
insert into t1 select a+16, b+16, c+16 from t1;
11+
insert into t1 select a+32, b+32, c+32 from t1;
12+
insert into t1 select a+64, b+64, c+64 from t1;
13+
insert into t1 select a+128, b+128, c+128 from t1;
14+
insert into t1 select a+256, b+256, c+256 from t1;
15+
insert into t1 select a+512, b+512, c+512 from t1;
16+
insert into t1 select a+1024, b+1024, c+1024 from t1;
17+
analyze table t1;
18+
Table Op Msg_type Msg_text
19+
test.t1 analyze status OK
20+
flush tables;
21+
#
22+
# repro issue when innodb_stats_update_online_ddl = true(by default)
23+
#
24+
SET DEBUG_SYNC = "innodb_inplace_alter_table_enter signal ddl_in_progress wait_for finish_ddl";
25+
alter table t1 drop column b;
26+
SET DEBUG_SYNC = "now wait_for ddl_in_progress";
27+
analyze table t1;
28+
Table Op Msg_type Msg_text
29+
test.t1 analyze status OK
30+
select stat_value from mysql.innodb_index_stats where table_name = 't1' and stat_name = 'n_diff_pfx01';
31+
stat_value
32+
0
33+
SET DEBUG_SYNC = "now signal finish_ddl";
34+
select stat_value from mysql.innodb_index_stats where table_name = 't1' and stat_name = 'n_diff_pfx01';
35+
stat_value
36+
2048
37+
#
38+
# don't update stats during DDL, it should return old stats
39+
#
40+
set global innodb_stats_update_online_ddl = false;
41+
SET DEBUG_SYNC = "innodb_inplace_alter_table_enter signal ddl_in_progress wait_for finish_ddl";
42+
alter table t1 drop column c;
43+
SET DEBUG_SYNC = "now wait_for ddl_in_progress";
44+
analyze table t1;
45+
Table Op Msg_type Msg_text
46+
test.t1 analyze status OK
47+
select stat_value from mysql.innodb_index_stats where table_name = 't1' and stat_name = 'n_diff_pfx01';
48+
stat_value
49+
2048
50+
SET DEBUG_SYNC = "now signal finish_ddl";
51+
select stat_value from mysql.innodb_index_stats where table_name = 't1' and stat_name = 'n_diff_pfx01';
52+
stat_value
53+
2048
54+
#
55+
# update SK column
56+
#
57+
set global innodb_stats_update_online_ddl = true;
58+
CREATE TABLE t2 (a int,
59+
b varchar(8) not NULL,
60+
c varchar(8) not NULL,
61+
primary key(a),
62+
key(b),
63+
key(c)) ENGINE=InnoDB;
64+
insert into t2 values(1,'1', '1'),(2,'2','2'),(3,'3','3'),(4,'4','4'),(5,'5','5'),(6,'6','6'),(7,'7','7'),(8,'8','8'),(9,'9','9');
65+
analyze table t2;
66+
Table Op Msg_type Msg_text
67+
test.t2 analyze status OK
68+
flush tables;
69+
SET DEBUG_SYNC = "innodb_inplace_alter_table_enter signal ddl_in_progress wait_for finish_ddl";
70+
alter table t2 MODIFY b varchar(8) NULL;
71+
SET DEBUG_SYNC = "now wait_for ddl_in_progress";
72+
analyze table t2;
73+
Table Op Msg_type Msg_text
74+
test.t2 analyze status OK
75+
select stat_value from mysql.innodb_index_stats where table_name = 't2' and stat_name = 'n_diff_pfx01';
76+
stat_value
77+
0
78+
9
79+
9
80+
SET DEBUG_SYNC = "now signal finish_ddl";
81+
select stat_value from mysql.innodb_index_stats where table_name = 't2' and stat_name = 'n_diff_pfx01';
82+
stat_value
83+
9
84+
9
85+
9
86+
set global innodb_stats_update_online_ddl = false;
87+
SET DEBUG_SYNC = "innodb_inplace_alter_table_enter signal ddl_in_progress wait_for finish_ddl";
88+
alter table t2 MODIFY c varchar(8) NULL;
89+
SET DEBUG_SYNC = "now wait_for ddl_in_progress";
90+
analyze table t2;
91+
Table Op Msg_type Msg_text
92+
test.t2 analyze status OK
93+
select stat_value from mysql.innodb_index_stats where table_name = 't2' and stat_name = 'n_diff_pfx01';
94+
stat_value
95+
9
96+
9
97+
9
98+
SET DEBUG_SYNC = "now signal finish_ddl";
99+
select stat_value from mysql.innodb_index_stats where table_name = 't2' and stat_name = 'n_diff_pfx01';
100+
stat_value
101+
9
102+
9
103+
9
104+
#
105+
# cleanup
106+
#
107+
drop table t1,t2;
108+
set global innodb_stats_update_online_ddl = default;
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
--source include/have_debug_sync.inc
2+
3+
--echo #
4+
--echo # update PK column
5+
--echo #
6+
CREATE TABLE t1 (a int,
7+
b int,
8+
c int) ENGINE=InnoDB;
9+
10+
insert into t1 values (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4);
11+
insert into t1 select a+4, b+4, c+4 from t1;
12+
insert into t1 select a+8, b+8, c+8 from t1;
13+
insert into t1 select a+16, b+16, c+16 from t1;
14+
insert into t1 select a+32, b+32, c+32 from t1;
15+
insert into t1 select a+64, b+64, c+64 from t1;
16+
insert into t1 select a+128, b+128, c+128 from t1;
17+
insert into t1 select a+256, b+256, c+256 from t1;
18+
insert into t1 select a+512, b+512, c+512 from t1;
19+
insert into t1 select a+1024, b+1024, c+1024 from t1;
20+
21+
analyze table t1;
22+
flush tables;
23+
24+
--echo #
25+
--echo # repro issue when innodb_stats_update_online_ddl = true(by default)
26+
--echo #
27+
connect (con1,localhost,root,,);
28+
SET DEBUG_SYNC = "innodb_inplace_alter_table_enter signal ddl_in_progress wait_for finish_ddl";
29+
send alter table t1 drop column b;
30+
31+
connection default;
32+
SET DEBUG_SYNC = "now wait_for ddl_in_progress";
33+
analyze table t1;
34+
select stat_value from mysql.innodb_index_stats where table_name = 't1' and stat_name = 'n_diff_pfx01';
35+
SET DEBUG_SYNC = "now signal finish_ddl";
36+
37+
connection con1;
38+
--reap
39+
select stat_value from mysql.innodb_index_stats where table_name = 't1' and stat_name = 'n_diff_pfx01';
40+
41+
42+
--echo #
43+
--echo # don't update stats during DDL, it should return old stats
44+
--echo #
45+
connection default;
46+
set global innodb_stats_update_online_ddl = false;
47+
48+
connection con1;
49+
SET DEBUG_SYNC = "innodb_inplace_alter_table_enter signal ddl_in_progress wait_for finish_ddl";
50+
send alter table t1 drop column c;
51+
52+
connection default;
53+
SET DEBUG_SYNC = "now wait_for ddl_in_progress";
54+
analyze table t1;
55+
select stat_value from mysql.innodb_index_stats where table_name = 't1' and stat_name = 'n_diff_pfx01';
56+
SET DEBUG_SYNC = "now signal finish_ddl";
57+
58+
connection con1;
59+
--reap
60+
select stat_value from mysql.innodb_index_stats where table_name = 't1' and stat_name = 'n_diff_pfx01';
61+
62+
63+
--echo #
64+
--echo # update SK column
65+
--echo #
66+
set global innodb_stats_update_online_ddl = true;
67+
CREATE TABLE t2 (a int,
68+
b varchar(8) not NULL,
69+
c varchar(8) not NULL,
70+
primary key(a),
71+
key(b),
72+
key(c)) ENGINE=InnoDB;
73+
74+
75+
insert into t2 values(1,'1', '1'),(2,'2','2'),(3,'3','3'),(4,'4','4'),(5,'5','5'),(6,'6','6'),(7,'7','7'),(8,'8','8'),(9,'9','9');
76+
analyze table t2;
77+
flush tables;
78+
79+
connection con1;
80+
SET DEBUG_SYNC = "innodb_inplace_alter_table_enter signal ddl_in_progress wait_for finish_ddl";
81+
send alter table t2 MODIFY b varchar(8) NULL;
82+
83+
connection default;
84+
SET DEBUG_SYNC = "now wait_for ddl_in_progress";
85+
analyze table t2;
86+
select stat_value from mysql.innodb_index_stats where table_name = 't2' and stat_name = 'n_diff_pfx01';
87+
SET DEBUG_SYNC = "now signal finish_ddl";
88+
89+
connection con1;
90+
--reap
91+
select stat_value from mysql.innodb_index_stats where table_name = 't2' and stat_name = 'n_diff_pfx01';
92+
93+
#
94+
# don't update stats during DDL, it should return old stats
95+
#
96+
connection default;
97+
set global innodb_stats_update_online_ddl = false;
98+
99+
connection con1;
100+
SET DEBUG_SYNC = "innodb_inplace_alter_table_enter signal ddl_in_progress wait_for finish_ddl";
101+
send alter table t2 MODIFY c varchar(8) NULL;
102+
103+
connection default;
104+
SET DEBUG_SYNC = "now wait_for ddl_in_progress";
105+
analyze table t2;
106+
select stat_value from mysql.innodb_index_stats where table_name = 't2' and stat_name = 'n_diff_pfx01';
107+
SET DEBUG_SYNC = "now signal finish_ddl";
108+
109+
connection con1;
110+
--reap
111+
select stat_value from mysql.innodb_index_stats where table_name = 't2' and stat_name = 'n_diff_pfx01';
112+
113+
--echo #
114+
--echo # cleanup
115+
--echo #
116+
connection default;
117+
drop table t1,t2;
118+
set global innodb_stats_update_online_ddl = default;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
SELECT @@innodb_stats_update_online_ddl;
2+
@@innodb_stats_update_online_ddl
3+
1
4+
SET GLOBAL innodb_stats_update_online_ddl=ON;
5+
SELECT @@innodb_stats_update_online_ddl;
6+
@@innodb_stats_update_online_ddl
7+
1
8+
SET GLOBAL innodb_stats_update_online_ddl=OFF;
9+
SELECT @@innodb_stats_update_online_ddl;
10+
@@innodb_stats_update_online_ddl
11+
0
12+
SET GLOBAL innodb_stats_update_online_ddl=1;
13+
SELECT @@innodb_stats_update_online_ddl;
14+
@@innodb_stats_update_online_ddl
15+
1
16+
SET GLOBAL innodb_stats_update_online_ddl=0;
17+
SELECT @@innodb_stats_update_online_ddl;
18+
@@innodb_stats_update_online_ddl
19+
0
20+
SET GLOBAL innodb_stats_update_online_ddl=123;
21+
ERROR 42000: Variable 'innodb_stats_update_online_ddl' can't be set to the value of '123'
22+
SET GLOBAL innodb_stats_update_online_ddl='foo';
23+
ERROR 42000: Variable 'innodb_stats_update_online_ddl' can't be set to the value of 'foo'
24+
SET GLOBAL innodb_stats_update_online_ddl=default;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#
2+
# innodb_stats_update_online_ddl
3+
#
4+
5+
6+
# show the default value
7+
SELECT @@innodb_stats_update_online_ddl;
8+
9+
# check that it is writeable
10+
SET GLOBAL innodb_stats_update_online_ddl=ON;
11+
SELECT @@innodb_stats_update_online_ddl;
12+
13+
SET GLOBAL innodb_stats_update_online_ddl=OFF;
14+
SELECT @@innodb_stats_update_online_ddl;
15+
16+
SET GLOBAL innodb_stats_update_online_ddl=1;
17+
SELECT @@innodb_stats_update_online_ddl;
18+
19+
SET GLOBAL innodb_stats_update_online_ddl=0;
20+
SELECT @@innodb_stats_update_online_ddl;
21+
22+
# should be a boolean
23+
-- error ER_WRONG_VALUE_FOR_VAR
24+
SET GLOBAL innodb_stats_update_online_ddl=123;
25+
26+
-- error ER_WRONG_VALUE_FOR_VAR
27+
SET GLOBAL innodb_stats_update_online_ddl='foo';
28+
29+
# restore the environment
30+
SET GLOBAL innodb_stats_update_online_ddl=default;

storage/innobase/dict/dict0stats.cc

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,14 +1696,22 @@ static bool dict_stats_analyze_index_low(ib_uint64_t &n_sample_pages,
16961696

16971697
DEBUG_PRINTF(" %s(index=%s)\n", __func__, index->name());
16981698

1699-
dict_stats_empty_index(index);
1700-
17011699
mtr_start(&mtr);
17021700

17031701
mtr_s_lock(dict_index_get_lock(index), &mtr);
17041702

17051703
size = btr_get_size(index, BTR_TOTAL_SIZE, &mtr);
17061704

1705+
/* Disable clear index stats if
1706+
- size is ULINT_UNDEFINED and
1707+
- srv_stats_update_online_ddl is false and
1708+
- online ddl is running in that index
1709+
*/
1710+
if (size != ULINT_UNDEFINED || srv_stats_update_online_ddl ||
1711+
!dict_index_is_online_ddl(index)) {
1712+
dict_stats_empty_index(index);
1713+
}
1714+
17071715
if (size != ULINT_UNDEFINED) {
17081716
index->stat_index_size = size;
17091717
size = btr_get_size(index, BTR_N_LEAF_PAGES, &mtr);

storage/innobase/handler/ha_innodb.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22237,6 +22237,12 @@ static MYSQL_SYSVAR_BOOL(stats_locked_reads, srv_stats_locked_reads,
2223722237
"Controls if InnoDB stats are locked for reading.",
2223822238
nullptr, nullptr, FALSE);
2223922239

22240+
static MYSQL_SYSVAR_BOOL(
22241+
stats_update_online_ddl, srv_stats_update_online_ddl, PLUGIN_VAR_OPCMDARG,
22242+
"Control If InnoDB should update index statistics, While related index is"
22243+
" being created or rebuilt.",
22244+
nullptr, nullptr, TRUE);
22245+
2224022246
static MYSQL_SYSVAR_BOOL(
2224122247
adaptive_hash_index, btr_search_enabled, PLUGIN_VAR_OPCMDARG,
2224222248
"Enable InnoDB adaptive hash index (enabled by default). "
@@ -23402,6 +23408,7 @@ static SYS_VAR *innobase_system_variables[] = {
2340223408
MYSQL_SYSVAR(stats_persistent_sample_pages),
2340323409
MYSQL_SYSVAR(stats_auto_recalc),
2340423410
MYSQL_SYSVAR(stats_locked_reads),
23411+
MYSQL_SYSVAR(stats_update_online_ddl),
2340523412
MYSQL_SYSVAR(adaptive_hash_index),
2340623413
MYSQL_SYSVAR(adaptive_hash_index_parts),
2340723414
MYSQL_SYSVAR(stats_method),

storage/innobase/include/srv0srv.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ extern bool srv_stats_persistent;
721721
extern unsigned long long srv_stats_persistent_sample_pages;
722722
extern bool srv_stats_auto_recalc;
723723
extern bool srv_stats_include_delete_marked;
724-
724+
extern bool srv_stats_update_online_ddl;
725725
extern ulong srv_checksum_algorithm;
726726

727727
extern double srv_max_buf_pool_modified_pct;

storage/innobase/srv/srv0srv.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ bool srv_stats_persistent = TRUE;
608608
bool srv_stats_include_delete_marked = FALSE;
609609
unsigned long long srv_stats_persistent_sample_pages = 20;
610610
bool srv_stats_auto_recalc = TRUE;
611-
611+
bool srv_stats_update_online_ddl = TRUE;
612612
ulong srv_replication_delay = 0;
613613
std::chrono::milliseconds get_srv_replication_delay() {
614614
return std::chrono::milliseconds{srv_replication_delay};

0 commit comments

Comments
 (0)