Details
-
Bug
-
Status: Closed (View Workflow)
-
Major
-
Resolution: Fixed
-
10.1(EOL), 10.2(EOL), 10.3(EOL)
Description
This is based on a customer testcase.
I wasn't able to construct an actual testcase so far, but I was able to get the invalid query plan by tampering with rows/cost estimates in the debugger.
The problem looks like this:
EXPLAIN
|
SELECT |
non_idex_col
|
FROM |
t2
|
WHERE |
key1part1 = 1500 and key1part2 IN (5, 60, 133, 387) AND |
pk1 = 700000
|
ORDER BY |
pk2 DESC LIMIT 1; |
+------+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
|
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|
+------+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
|
| 1 | SIMPLE | t2 | index | PRIMARY,key1 | PRIMARY | 6 | NULL | 26 | Using where |
|
+------+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
|
The plan uses a full index scan, even if there is a potential ref (or range) access over the PRIMARY key: pk1 = 700000. Costs and selectivity numbers are irrelevant - if we pick to do a full index scan, then range on that index will not be slower.
The optimizer actually has this logic and was expected to use range access. It even typically does. Getting the above query plan requires some peculiar relationship between costs of range/ref accesses through key1 and primary key.
Steps to reproduce:
1. Fill the dataset
2. In the debugger, put a breakpoint in get_key_scans_params after this call:
found_records= check_quick_select(param, idx, read_index_only, key,
update_tbl_stats, &mrr_flags,
&buf_size, &cost);
Condition for breakpoint: keynr == 1
Actions to do:
set cost.io_count=10
set found_records=found_records * 0.5
Continue execution. It should be that best_idx=1 after loop exit.
3. Put a breakpoint in best_access_path BEFORE this code:
{
best= tmp;
best_records= records;
best_key= start_key;
best_max_key_part= max_key_part;
best_ref_depends_map= found_ref;
}
On the second breakpoint hit, you will see tmp=10291 or something like that.
Actions to do:
set tmp=200
It should pick ref access on key1
Then continue execution and test_if_skip_sort_order/test_if_cheaper_ordering will choose to switch to PRIMARY key and use index scan.