MDEV-35443: opt_search_plan_for_table() may degrade to full table scan
opt_calc_index_goodness(): Correct an inaccurate condition. We can very well use a clustered index of a table that is subject to online rebuild. But we must not choose an index that has not been committed (it is a secondary index that was not fully created) or that is corrupted or not a normal B-tree index. opt_search_plan_for_table(): Remove some redundant code, now that opt_calc_index_goodness() checks against corrupted indexes. The test case allows this code to be exercised. The main observation in the following: ./mtr --rr innodb.stats_persistent rr replay var/log/mysqld.1.rr/latest-trace should be that when opt_search_plan_for_table() is being invoked by dict_stats_update_persistent() on the being-altered statistics table in the 2nd call after ha_innobase::inplace_alter_table(), and the fix in opt_calc_index_goodness() is absent, it would choose the code path if (n_fields == 0), that is, a full table scan, instead of searching for the record. The GDB commands to execute in "rr replay" would be as follows: break ha_innobase::inplace_alter_table continue break opt_search_plan_for_table continue continue next next … Reviewed by: Vladislav Lesin
This commit is contained in:
parent
a226f12675
commit
e5c4c0842d
@ -6,14 +6,23 @@ SET DEBUG_SYNC='dict_stats_update_persistent SIGNAL stop WAIT_FOR go';
|
|||||||
ANALYZE TABLE t1;
|
ANALYZE TABLE t1;
|
||||||
connect con1, localhost, root;
|
connect con1, localhost, root;
|
||||||
SET DEBUG_SYNC='now WAIT_FOR stop';
|
SET DEBUG_SYNC='now WAIT_FOR stop';
|
||||||
|
connect con2, localhost, root;
|
||||||
|
SET DEBUG_SYNC='innodb_inplace_alter_table_enter SIGNAL astop WAIT_FOR ago';
|
||||||
|
ALTER TABLE mysql.innodb_index_stats FORCE;
|
||||||
|
connection con1;
|
||||||
SELECT SUM(DATA_LENGTH+INDEX_LENGTH) FROM information_schema.TABLES WHERE ENGINE='InnoDB';
|
SELECT SUM(DATA_LENGTH+INDEX_LENGTH) FROM information_schema.TABLES WHERE ENGINE='InnoDB';
|
||||||
SUM(DATA_LENGTH+INDEX_LENGTH)
|
SUM(DATA_LENGTH+INDEX_LENGTH)
|
||||||
SUM
|
SUM
|
||||||
|
SET DEBUG_SYNC='now WAIT_FOR astop';
|
||||||
SET DEBUG_SYNC='now SIGNAL go';
|
SET DEBUG_SYNC='now SIGNAL go';
|
||||||
disconnect con1;
|
disconnect con1;
|
||||||
connection default;
|
connection default;
|
||||||
Table Op Msg_type Msg_text
|
Table Op Msg_type Msg_text
|
||||||
test.t1 analyze status Engine-independent statistics collected
|
test.t1 analyze status Engine-independent statistics collected
|
||||||
test.t1 analyze status OK
|
test.t1 analyze status OK
|
||||||
|
SET DEBUG_SYNC='now SIGNAL ago';
|
||||||
|
connection con2;
|
||||||
|
disconnect con2;
|
||||||
|
connection default;
|
||||||
SET DEBUG_SYNC= 'RESET';
|
SET DEBUG_SYNC= 'RESET';
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
@ -14,14 +14,25 @@ SET DEBUG_SYNC='dict_stats_update_persistent SIGNAL stop WAIT_FOR go';
|
|||||||
--connect(con1, localhost, root)
|
--connect(con1, localhost, root)
|
||||||
SET DEBUG_SYNC='now WAIT_FOR stop';
|
SET DEBUG_SYNC='now WAIT_FOR stop';
|
||||||
|
|
||||||
|
--connect(con2, localhost, root)
|
||||||
|
SET DEBUG_SYNC='innodb_inplace_alter_table_enter SIGNAL astop WAIT_FOR ago';
|
||||||
|
send ALTER TABLE mysql.innodb_index_stats FORCE;
|
||||||
|
|
||||||
|
--connection con1
|
||||||
--replace_column 1 SUM
|
--replace_column 1 SUM
|
||||||
SELECT SUM(DATA_LENGTH+INDEX_LENGTH) FROM information_schema.TABLES WHERE ENGINE='InnoDB';
|
SELECT SUM(DATA_LENGTH+INDEX_LENGTH) FROM information_schema.TABLES WHERE ENGINE='InnoDB';
|
||||||
|
|
||||||
|
SET DEBUG_SYNC='now WAIT_FOR astop';
|
||||||
SET DEBUG_SYNC='now SIGNAL go';
|
SET DEBUG_SYNC='now SIGNAL go';
|
||||||
--disconnect con1
|
--disconnect con1
|
||||||
|
|
||||||
--connection default
|
--connection default
|
||||||
--reap
|
--reap
|
||||||
|
SET DEBUG_SYNC='now SIGNAL ago';
|
||||||
|
--connection con2
|
||||||
|
reap;
|
||||||
|
--disconnect con2
|
||||||
|
--connection default
|
||||||
SET DEBUG_SYNC= 'RESET';
|
SET DEBUG_SYNC= 'RESET';
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
@ -1203,6 +1203,14 @@ public:
|
|||||||
/** @return whether this is the change buffer */
|
/** @return whether this is the change buffer */
|
||||||
bool is_ibuf() const { return UNIV_UNLIKELY(type & DICT_IBUF); }
|
bool is_ibuf() const { return UNIV_UNLIKELY(type & DICT_IBUF); }
|
||||||
|
|
||||||
|
/** @return whether this is a normal, non-virtual B-tree index
|
||||||
|
(not the change buffer, not SPATIAL or FULLTEXT) */
|
||||||
|
bool is_normal_btree() const noexcept {
|
||||||
|
return UNIV_LIKELY(!(type & (DICT_IBUF | DICT_SPATIAL
|
||||||
|
| DICT_FTS | DICT_CORRUPT
|
||||||
|
| DICT_VIRTUAL)));
|
||||||
|
}
|
||||||
|
|
||||||
/** @return whether the index includes virtual columns */
|
/** @return whether the index includes virtual columns */
|
||||||
bool has_virtual() const { return type & DICT_VIRTUAL; }
|
bool has_virtual() const { return type & DICT_VIRTUAL; }
|
||||||
|
|
||||||
|
@ -340,9 +340,7 @@ opt_calc_index_goodness(
|
|||||||
ulint op;
|
ulint op;
|
||||||
ulint j;
|
ulint j;
|
||||||
|
|
||||||
/* At least for now we don't support using FTS indexes for queries
|
if (!index->is_normal_btree() || !index->is_committed()) {
|
||||||
done through InnoDB's own SQL parser. */
|
|
||||||
if (dict_index_is_online_ddl(index) || (index->type & DICT_FTS)) {
|
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -572,11 +570,10 @@ opt_search_plan_for_table(
|
|||||||
/* Calculate goodness for each index of the table */
|
/* Calculate goodness for each index of the table */
|
||||||
|
|
||||||
index = dict_table_get_first_index(table);
|
index = dict_table_get_first_index(table);
|
||||||
best_index = index; /* Eliminate compiler warning */
|
best_index = index;
|
||||||
best_goodness = 0;
|
best_goodness = 0;
|
||||||
|
|
||||||
/* should be do ... until ? comment by Jani */
|
do {
|
||||||
while (index) {
|
|
||||||
goodness = opt_calc_index_goodness(index, sel_node, i,
|
goodness = opt_calc_index_goodness(index, sel_node, i,
|
||||||
index_plan, &last_op);
|
index_plan, &last_op);
|
||||||
if (goodness > best_goodness) {
|
if (goodness > best_goodness) {
|
||||||
@ -590,8 +587,8 @@ opt_search_plan_for_table(
|
|||||||
best_last_op = last_op;
|
best_last_op = last_op;
|
||||||
}
|
}
|
||||||
|
|
||||||
dict_table_next_uncorrupted_index(index);
|
index = dict_table_get_next_index(index);
|
||||||
}
|
} while (index);
|
||||||
|
|
||||||
plan->index = best_index;
|
plan->index = best_index;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user