Skip to content

Commit 992d782

Browse files
committed
MDEV-6735: Range checked for each record used with key (also MDEV-7786, MDEV-7923)
"Range Checked for Each Record" should be only employed when the other option would be cross-product join (i.e. the other option is so bad that we hardly risk anything). Previous logic was: use RCfER if there are no possible quick selects, or quick select would read > 100 rows. Also, it didn't always work as expected due to range optimizer changing table->quick_keys and us looking at sel->quick_keys. Another angle is that recent versions have enabled use of Join Buffering in e.g. outer joins. This further reduces the range of cases where RCfER should be used. We are still unable to estimate the cost of RCfER with any precision, so now changing the condition of "no quick select or quick->records> 100" to a hopefully better condition "no quick select or quick would cost more than full table scan".
1 parent 5d57e2d commit 992d782

File tree

3 files changed

+102
-2
lines changed

3 files changed

+102
-2
lines changed

mysql-test/r/range_innodb.result

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#
2+
# Range optimizer (and related) tests that need InnoDB.
3+
#
4+
drop table if exists t0, t1, t2;
5+
#
6+
# MDEV-6735: Range checked for each record used with key
7+
#
8+
create table t0(a int);
9+
insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
10+
create table t1(a int);
11+
insert into t1 select A.a + B.a* 10 + C.a * 100 + D.a * 1000
12+
from t0 A, t0 B, t0 C, t0 D;
13+
create table t2 (
14+
a int,
15+
b int,
16+
filler1 char(100),
17+
filler2 char(100),
18+
filler3 char(100),
19+
filler4 char(100),
20+
key(a),
21+
key(b)
22+
) engine=innodb;
23+
insert into t2
24+
select
25+
a,a,
26+
repeat('0123456789', 10),
27+
repeat('0123456789', 10),
28+
repeat('0123456789', 10),
29+
repeat('0123456789', 10)
30+
from t1;
31+
analyze table t2;
32+
Table Op Msg_type Msg_text
33+
test.t2 analyze status OK
34+
# The following must not use "Range checked for each record":
35+
explain select * from t0 left join t2 on t2.a <t0.a and t2.b between 50 and 250;
36+
id select_type table type possible_keys key key_len ref rows Extra
37+
1 SIMPLE t0 ALL NULL NULL NULL NULL 10
38+
1 SIMPLE t2 range a,b b 5 NULL 201 Using where; Using join buffer (flat, BNL join)
39+
drop table t0,t1,t2;

mysql-test/t/range_innodb.test

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
--echo #
2+
--echo # Range optimizer (and related) tests that need InnoDB.
3+
--echo #
4+
5+
--source include/have_innodb.inc
6+
7+
--disable_warnings
8+
drop table if exists t0, t1, t2;
9+
--enable_warnings
10+
11+
--echo #
12+
--echo # MDEV-6735: Range checked for each record used with key
13+
--echo #
14+
15+
create table t0(a int);
16+
insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
17+
18+
create table t1(a int);
19+
insert into t1 select A.a + B.a* 10 + C.a * 100 + D.a * 1000
20+
from t0 A, t0 B, t0 C, t0 D;
21+
22+
create table t2 (
23+
a int,
24+
b int,
25+
filler1 char(100),
26+
filler2 char(100),
27+
filler3 char(100),
28+
filler4 char(100),
29+
key(a),
30+
key(b)
31+
) engine=innodb;
32+
33+
insert into t2
34+
select
35+
a,a,
36+
repeat('0123456789', 10),
37+
repeat('0123456789', 10),
38+
repeat('0123456789', 10),
39+
repeat('0123456789', 10)
40+
from t1;
41+
42+
analyze table t2;
43+
--echo # The following must not use "Range checked for each record":
44+
explain select * from t0 left join t2 on t2.a <t0.a and t2.b between 50 and 250;
45+
46+
drop table t0,t1,t2;
47+

sql/sql_select.cc

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9013,10 +9013,24 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
90139013
if (!sel->quick_keys.is_subset(tab->checked_keys) ||
90149014
!sel->needed_reg.is_subset(tab->checked_keys))
90159015
{
9016+
/*
9017+
"Range checked for each record" is a "last resort" access method
9018+
that should only be used when the other option is a cross-product
9019+
join.
9020+
9021+
We use the following condition (it's approximate):
9022+
1. There are potential keys for (sel->needed_reg)
9023+
2. There were no possible ways to construct a quick select, or
9024+
the quick select would be more expensive than the full table
9025+
scan.
9026+
*/
90169027
tab->use_quick= (!sel->needed_reg.is_clear_all() &&
90179028
(sel->quick_keys.is_clear_all() ||
9018-
(sel->quick &&
9019-
(sel->quick->records >= 100L)))) ?
9029+
(sel->quick &&
9030+
sel->quick->read_time >
9031+
tab->table->file->scan_time() +
9032+
tab->table->file->stats.records/TIME_FOR_COMPARE
9033+
))) ?
90209034
2 : 1;
90219035
sel->read_tables= used_tables & ~current_map;
90229036
sel->quick_keys.clear_all();

0 commit comments

Comments
 (0)