Skip to content

Commit b47bd3f

Browse files
committed
MDEV-33875: ORDER BY DESC causes ROWID Filter slowdown
Rowid Filter cannot be used with reverse-ordered scans, for the same reason as IndexConditionPushdown cannot be. test_if_skip_sort_order() already has logic to disable ICP when setting up a reverse-ordered scan. Added logic to also disable Rowid Filter in this case, factored out the code into prepare_for_reverse_ordered_access(), and added a comment describing the cause of this limitation.
1 parent 956bcf8 commit b47bd3f

File tree

3 files changed

+196
-28
lines changed

3 files changed

+196
-28
lines changed

mysql-test/main/rowid_filter_innodb.result

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3947,3 +3947,68 @@ count(*)
39473947
SET optimizer_switch=@save_optimizer_switch;
39483948
DROP TABLE t0, t1;
39493949
# End of 10.4 tests
3950+
#
3951+
# MDEV-33875: ORDER BY DESC causes ROWID Filter slowdown
3952+
#
3953+
create table t1 (
3954+
pk int primary key auto_increment,
3955+
a int,
3956+
b int,
3957+
f1 varchar(200),
3958+
f2 varchar(200),
3959+
f3 varchar(200),
3960+
f4 varchar(200),
3961+
f5 varchar(200),
3962+
key(a, pk),
3963+
key(b)
3964+
) engine=innodb;
3965+
insert into t1 (a,b,f1, f2, f3, f4) select
3966+
seq, seq,
3967+
repeat('1-', 100),
3968+
repeat('2-', 100),
3969+
repeat('3-', 100),
3970+
repeat('4-', 100)
3971+
from
3972+
seq_1_to_5000;
3973+
insert into t1 (a,b,f1, f2, f3, f4)select
3974+
30100, 30100,
3975+
'abcd','abcd','abcd','abcd'
3976+
from
3977+
seq_1_to_250;
3978+
insert into t1 (a,b,f1) values ( 110, 100, 12345);
3979+
analyze table t1;
3980+
Table Op Msg_type Msg_text
3981+
test.t1 analyze status Engine-independent statistics collected
3982+
test.t1 analyze status OK
3983+
# The following must NOT use Rowid Filter:
3984+
analyze format=json select * from t1
3985+
where
3986+
a =30100 and b in (30100,30101,30102)
3987+
order by
3988+
pk desc;
3989+
ANALYZE
3990+
{
3991+
"query_block": {
3992+
"select_id": 1,
3993+
"r_loops": 1,
3994+
"r_total_time_ms": "REPLACED",
3995+
"table": {
3996+
"table_name": "t1",
3997+
"access_type": "ref",
3998+
"possible_keys": ["a", "b"],
3999+
"key": "a",
4000+
"key_length": "5",
4001+
"used_key_parts": ["a"],
4002+
"ref": ["const"],
4003+
"r_loops": 1,
4004+
"rows": 250,
4005+
"r_rows": 250,
4006+
"r_table_time_ms": "REPLACED",
4007+
"r_other_time_ms": "REPLACED",
4008+
"filtered": 4.799086094,
4009+
"r_filtered": 100,
4010+
"attached_condition": "t1.a <=> 30100 and t1.b in (30100,30101,30102)"
4011+
}
4012+
}
4013+
}
4014+
drop table t1;

mysql-test/main/rowid_filter_innodb.test

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,3 +751,47 @@ SET optimizer_switch=@save_optimizer_switch;
751751
DROP TABLE t0, t1;
752752

753753
--echo # End of 10.4 tests
754+
755+
--echo #
756+
--echo # MDEV-33875: ORDER BY DESC causes ROWID Filter slowdown
757+
--echo #
758+
create table t1 (
759+
pk int primary key auto_increment,
760+
a int,
761+
b int,
762+
f1 varchar(200),
763+
f2 varchar(200),
764+
f3 varchar(200),
765+
f4 varchar(200),
766+
f5 varchar(200),
767+
key(a, pk),
768+
key(b)
769+
) engine=innodb;
770+
771+
insert into t1 (a,b,f1, f2, f3, f4) select
772+
seq, seq,
773+
repeat('1-', 100),
774+
repeat('2-', 100),
775+
repeat('3-', 100),
776+
repeat('4-', 100)
777+
from
778+
seq_1_to_5000;
779+
780+
insert into t1 (a,b,f1, f2, f3, f4)select
781+
30100, 30100,
782+
'abcd','abcd','abcd','abcd'
783+
from
784+
seq_1_to_250;
785+
insert into t1 (a,b,f1) values ( 110, 100, 12345);
786+
analyze table t1;
787+
788+
--echo # The following must NOT use Rowid Filter:
789+
--source include/analyze-format.inc
790+
analyze format=json select * from t1
791+
where
792+
a =30100 and b in (30100,30101,30102)
793+
order by
794+
pk desc;
795+
796+
drop table t1;
797+

sql/sql_select.cc

Lines changed: 87 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23957,6 +23957,90 @@ void compute_part_of_sort_key_for_equals(JOIN *join, TABLE *table,
2395723957
}
2395823958

2395923959

23960+
/*
23961+
@brief
23962+
This is called when switching table access to produce records
23963+
in reverse order.
23964+
23965+
@detail
23966+
- Disable "Range checked for each record" (Is this strictly necessary
23967+
here?)
23968+
- Disable Index Condition Pushdown and Rowid Filtering.
23969+
23970+
IndexConditionPushdownAndReverseScans, RowidFilteringAndReverseScans:
23971+
Suppose we're computing
23972+
23973+
select * from t1
23974+
where
23975+
key1 between 10 and 20 and extra_condition
23976+
order by key1 desc
23977+
23978+
here the range access uses a reverse-ordered scan on (1 <= key1 <= 10) and
23979+
extra_condition is checked by either ICP or Rowid Filtering.
23980+
23981+
Also suppose that extra_condition happens to be false for rows of t1 that
23982+
do not satisfy the "10 <= key1 <= 20" condition.
23983+
23984+
For forward ordered range scan, the SQL layer will make these calls:
23985+
23986+
h->read_range_first(RANGE(10 <= key1 <= 20));
23987+
while (h->read_range_next()) { ... }
23988+
23989+
The storage engine sees the end endpoint of "key1<=20" and can stop scanning
23990+
as soon as it encounters a row with key1>20.
23991+
23992+
For backward-ordered range scan, the SQL layer will make these calls:
23993+
23994+
h->index_read_map(key1=20, HA_READ_PREFIX_LAST_OR_PREV);
23995+
while (h->index_prev()) {
23996+
if (cmp_key(h->record, "key1=10" )<0)
23997+
break; // end of range
23998+
...
23999+
}
24000+
24001+
Note that the check whether we've walked beyond the key=10 endpoint is
24002+
made at the SQL layer. The storage engine has no information about the left
24003+
endpoint of the interval we're scanning. If all rows before that endpoint
24004+
do not satisfy ICP condition or do not pass the Rowid Filter, the storage
24005+
engine will enumerate the records until the table start.
24006+
24007+
In MySQL, the API is extended with set_end_range() call so that the storage
24008+
engine "knows" when to stop scanning.
24009+
*/
24010+
24011+
static void prepare_for_reverse_ordered_access(JOIN_TAB *tab)
24012+
{
24013+
/* Cancel "Range checked for each record" */
24014+
if (tab->use_quick == 2)
24015+
{
24016+
tab->use_quick= 1;
24017+
tab->read_first_record= join_init_read_record;
24018+
}
24019+
/*
24020+
Cancel Pushed Index Condition, as it doesn't work for reverse scans.
24021+
*/
24022+
if (tab->select && tab->select->pre_idx_push_select_cond)
24023+
{
24024+
tab->set_cond(tab->select->pre_idx_push_select_cond);
24025+
tab->table->file->cancel_pushed_idx_cond();
24026+
}
24027+
/*
24028+
The same with Rowid Filter: it doesn't work with reverse scans so cancel
24029+
it, too.
24030+
*/
24031+
{
24032+
/*
24033+
Rowid Filter is initialized at a later stage. It is not pushed to
24034+
the storage engine yet:
24035+
*/
24036+
DBUG_ASSERT(!tab->table->file->pushed_rowid_filter);
24037+
tab->range_rowid_filter_info= NULL;
24038+
delete tab->rowid_filter;
24039+
tab->rowid_filter= NULL;
24040+
}
24041+
}
24042+
24043+
2396024044
/**
2396124045
Test if we can skip the ORDER BY by using an index.
2396224046

@@ -24409,23 +24493,11 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
2440924493
tab->limit= 0;
2441024494
goto use_filesort; // Reverse sort failed -> filesort
2441124495
}
24412-
/*
24413-
Cancel Pushed Index Condition, as it doesn't work for reverse scans.
24414-
*/
24415-
if (tab->select && tab->select->pre_idx_push_select_cond)
24416-
{
24417-
tab->set_cond(tab->select->pre_idx_push_select_cond);
24418-
tab->table->file->cancel_pushed_idx_cond();
24419-
}
24496+
prepare_for_reverse_ordered_access(tab);
24497+
2442024498
if (select->quick == save_quick)
2442124499
save_quick= 0; // make_reverse() consumed it
2442224500
select->set_quick(tmp);
24423-
/* Cancel "Range checked for each record" */
24424-
if (tab->use_quick == 2)
24425-
{
24426-
tab->use_quick= 1;
24427-
tab->read_first_record= join_init_read_record;
24428-
}
2442924501
}
2443024502
else if (tab->type != JT_NEXT && tab->type != JT_REF_OR_NULL &&
2443124503
tab->ref.key >= 0 && tab->ref.key_parts <= used_key_parts)
@@ -24438,20 +24510,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
2443824510
*/
2443924511
tab->read_first_record= join_read_last_key;
2444024512
tab->read_record.read_record_func= join_read_prev_same;
24441-
/* Cancel "Range checked for each record" */
24442-
if (tab->use_quick == 2)
24443-
{
24444-
tab->use_quick= 1;
24445-
tab->read_first_record= join_init_read_record;
24446-
}
24447-
/*
24448-
Cancel Pushed Index Condition, as it doesn't work for reverse scans.
24449-
*/
24450-
if (tab->select && tab->select->pre_idx_push_select_cond)
24451-
{
24452-
tab->set_cond(tab->select->pre_idx_push_select_cond);
24453-
tab->table->file->cancel_pushed_idx_cond();
24454-
}
24513+
prepare_for_reverse_ordered_access(tab);
2445524514
}
2445624515
}
2445724516
else if (select && select->quick)

0 commit comments

Comments
 (0)