Commit ce7ffe61 authored by Igor Babaev's avatar Igor Babaev Committed by Oleksandr Byelkin

MDEV-26301 Split optimization refills temporary table too many times

This patch optimizes the number of refills for the lateral derived table
to which a materialized derived table subject to split optimization is
is converted. This optimized number of refills is now considered as the
expected number of refills of the materialized derived table when searching
for the best possible splitting of the table.
parent ec79f377
This diff is collapsed.
......@@ -227,3 +227,216 @@ where t3.b > 15;
drop table t3, t4;
--echo # End of 10.3 tests
--source include/have_sequence.inc
--echo #
--echo # MDEV-26301: Split optimization refills temporary table too many times
--echo #
# 5 values
create table t1(a int, b int);
insert into t1 select seq,seq from seq_1_to_5;
# 5 value groups of size 2 each
create table t2(a int, b int, key(a));
insert into t2
select A.seq,B.seq from seq_1_to_25 A, seq_1_to_2 B;
# 5 value groups of size 3 each
create table t3(a int, b int, key(a));
insert into t3
select A.seq,B.seq from seq_1_to_5 A, seq_1_to_3 B;
analyze table t1,t2,t3 persistent for all;
explain
select * from
(t1 left join t2 on t2.a=t1.b) left join t3 on t3.a=t1.b;
# Now, create tables for Groups.
create table t10 (
grp_id int,
col1 int,
key(grp_id)
);
# 100 groups of 100 values each
insert into t10
select
A.seq,
B.seq
from
seq_1_to_100 A,
seq_1_to_100 B;
# and X10 multiplier
create table t11 (
col1 int,
col2 int
);
insert into t11
select A.seq, A.seq from seq_1_to_10 A;
analyze table t10,t11 persistent for all;
let $q1=
select * from
(
(t1 left join t2 on t2.a=t1.b)
left join t3 on t3.a=t1.b
) left join (select grp_id, count(*)
from t10 left join t11 on t11.col1=t10.col1
group by grp_id) T on T.grp_id=t1.b;
eval
explain $q1;
--echo # The important part in the below output is:
--echo # "lateral": 1,
--echo # "query_block": {
--echo # "select_id": 2,
--echo # "r_loops": 5, <-- must be 5, not 30.
--source include/analyze-format.inc
eval
analyze format=json $q1;
create table t21 (pk int primary key);
insert into t21 values (1),(2),(3);
create table t22 (pk int primary key);
insert into t22 values (1),(2),(3);
# Same as above but throw in a couple of const tables.
explain
select * from
t21, t22,
(
(t1 left join t2 on t2.a=t1.b)
left join t3 on t3.a=t1.b
) left join (select grp_id, count(*)
from t10 left join t11 on t11.col1=t10.col1
group by grp_id) T on T.grp_id=t1.b
where
t21.pk=1 and t22.pk=2;
explain
select * from
t21,
(
(t1 left join t2 on t2.a=t1.b)
left join t3 on t3.a=t1.b
) left join (select grp_id, count(*)
from
t22 join t10 left join t11 on t11.col1=t10.col1
where
t22.pk=1
group by grp_id) T on T.grp_id=t1.b
where
t21.pk=1;
# And also add a non-const table
create table t5 (
pk int primary key
);
insert into t5 select seq from seq_1_to_1000;
explain
select * from
t21,
(
(((t1 join t5 on t5.pk=t1.b)) left join t2 on t2.a=t1.b)
left join t3 on t3.a=t1.b
) left join (select grp_id, count(*)
from
t22 join t10 left join t11 on t11.col1=t10.col1
where
t22.pk=1
group by grp_id) T on T.grp_id=t1.b
where
t21.pk=1;
drop table t1,t2,t3,t5, t10, t11, t21, t22;
# 5 values
create table t1(a int, b int);
insert into t1 select seq,seq from seq_1_to_5;
# 5 value groups of size 2 each
create table t2(a int, b int, key(a));
insert into t2
select A.seq,B.seq from seq_1_to_25 A, seq_1_to_2 B;
# 5 value groups of size 3 each
create table t3(a int, b int, key(a));
insert into t3
select A.seq,B.seq from seq_1_to_5 A, seq_1_to_3 B;
analyze table t1,t2,t3 persistent for all;
create table t10 (
grp_id int,
col1 int,
key(grp_id)
);
# 100 groups of 100 values each
insert into t10
select
A.seq,
B.seq
from
seq_1_to_100 A,
seq_1_to_100 B;
# and X10 multiplier
create table t11 (
col1 int,
col2 int
);
insert into t11
select A.seq, A.seq from seq_1_to_10 A;
analyze table t10,t11 persistent for all;
let $q=
select *
from
(
(t1 left join t2 on t2.a=t1.b)
left join
t3
on t3.a=t1.b
)
left join
(
select grp_id, count(*)
from t10 left join t11 on t11.col1=t10.col1
group by grp_id
)dt
on dt.grp_id=t1.b;
eval explain $q;
eval $q;
set join_cache_level=4;
eval explain $q;
eval $q;
set join_cache_level=default;
drop index a on t2;
drop index a on t3;
eval explain $q;
eval $q;
drop table t1,t2,t3;
drop table t10, t11;
--echo # End of 10.4 tests
This diff is collapsed.
......@@ -7298,6 +7298,7 @@ void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
join->positions[idx].records_read=1.0; /* This is a const table */
join->positions[idx].cond_selectivity= 1.0;
join->positions[idx].ref_depend_map= 0;
join->positions[idx].partial_join_cardinality= 1;
// join->positions[idx].loosescan_key= MAX_KEY; /* Not a LooseScan */
join->positions[idx].sj_strategy= SJ_OPT_NONE;
......@@ -7315,6 +7316,7 @@ void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
}
join->best_ref[idx]=table;
join->positions[idx].spl_plan= 0;
join->positions[idx].spl_pd_boundary= 0;
}
......@@ -7431,6 +7433,7 @@ best_access_path(JOIN *join,
MY_BITMAP *eq_join_set= &s->table->eq_join_set;
KEYUSE *hj_start_key= 0;
SplM_plan_info *spl_plan= 0;
table_map spl_pd_boundary= 0;
Range_rowid_filter_cost_info *filter= 0;
const char* cause= NULL;
enum join_type best_type= JT_UNKNOWN, type= JT_UNKNOWN;
......@@ -7448,7 +7451,9 @@ best_access_path(JOIN *join,
loose_scan_opt.init(join, s, remaining_tables);
if (s->table->is_splittable())
spl_plan= s->choose_best_splitting(record_count, remaining_tables);
spl_plan= s->choose_best_splitting(idx,
remaining_tables,
&spl_pd_boundary);
if (s->keyuse)
{ /* Use key if possible */
......@@ -8243,8 +8248,9 @@ best_access_path(JOIN *join,
best_filter= filter;
/* range/index_merge/ALL/index access method are "independent", so: */
best_ref_depends_map= 0;
best_uses_jbuf= MY_TEST(!disable_jbuf && !((s->table->map &
join->outer_join)));
best_uses_jbuf= MY_TEST(!disable_jbuf &&
(join->allowed_outer_join_with_cache ||
!(s->table->map & join->outer_join)));
spl_plan= 0;
best_type= type;
}
......@@ -8266,6 +8272,7 @@ best_access_path(JOIN *join,
pos->loosescan_picker.loosescan_key= MAX_KEY;
pos->use_join_buffer= best_uses_jbuf;
pos->spl_plan= spl_plan;
pos->spl_pd_boundary= !spl_plan ? 0 : spl_pd_boundary;
pos->range_rowid_filter_info= best_filter;
loose_scan_opt.save_to_position(s, loose_scan_pos);
......@@ -8795,6 +8802,9 @@ optimize_straight_join(JOIN *join, table_map join_tables)
pushdown_cond_selectivity= table_cond_selectivity(join, idx, s,
join_tables);
position->cond_selectivity= pushdown_cond_selectivity;
double partial_join_cardinality= record_count *
pushdown_cond_selectivity;
join->positions[idx].partial_join_cardinality= partial_join_cardinality;
++idx;
}
......@@ -9833,6 +9843,8 @@ best_extension_by_limited_search(JOIN *join,
double partial_join_cardinality= current_record_count *
pushdown_cond_selectivity;
join->positions[idx].partial_join_cardinality= partial_join_cardinality;
if ( (search_depth > 1) && (remaining_tables & ~real_table_bit) & allowed_tables )
{ /* Recursively expand the current partial plan */
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
......@@ -12860,6 +12872,9 @@ uint check_join_cache_usage(JOIN_TAB *tab,
join->return_tab= 0;
if (tab->no_forced_join_cache)
return 0;
/*
Don't use join cache if @@join_cache_level==0 or this table is the first
one join suborder (either at top level or inside a bush)
......@@ -13825,7 +13840,8 @@ bool JOIN_TAB::preread_init()
DBUG_RETURN(TRUE);
if (!(derived->get_unit()->uncacheable & UNCACHEABLE_DEPENDENT) ||
derived->is_nonrecursive_derived_with_rec_ref())
derived->is_nonrecursive_derived_with_rec_ref() ||
is_split_derived)
preread_init_done= TRUE;
if (select && select->quick)
select->quick->replace_handler(table->file);
......@@ -17276,6 +17292,9 @@ void optimize_wo_join_buffering(JOIN *join, uint first_tab, uint last_tab,
reopt_remaining_tables &
~real_table_bit);
}
double partial_join_cardinality= rec_count *
pushdown_cond_selectivity;
join->positions[i].partial_join_cardinality= partial_join_cardinality;
(*outer_rec_count) *= pushdown_cond_selectivity;
if (!rs->emb_sj_nest)
*outer_rec_count= COST_MULT(*outer_rec_count, pos.records_read);
......@@ -20764,6 +20783,16 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
{
DBUG_ENTER("sub_select");
if (join_tab->split_derived_to_update && !end_of_records)
{
table_map tab_map= join_tab->split_derived_to_update;
for (uint i= 0; tab_map; i++, tab_map>>= 1)
{
if (tab_map & 1)
join->map2table[i]->preread_init_done= false;
}
}
if (join_tab->last_inner)
{
JOIN_TAB *last_inner_tab= join_tab->last_inner;
......
......@@ -394,6 +394,8 @@ typedef struct st_join_table {
*/
bool idx_cond_fact_out;
bool use_join_cache;
/* TRUE <=> it is prohibited to join this table using join buffer */
bool no_forced_join_cache;
uint used_join_cache_level;
ulong join_buffer_size_limit;
JOIN_CACHE *cache;
......@@ -520,6 +522,16 @@ typedef struct st_join_table {
bool preread_init_done;
/* true <=> split optimization has been applied to this materialized table */
bool is_split_derived;
/*
Bitmap of split materialized derived tables that can be filled just before
this join table is to be joined. All parameters of the split derived tables
belong to tables preceding this join table.
*/
table_map split_derived_to_update;
/*
Cost info to the range filter used when joining this join table
(Defined when the best join order has been already chosen)
......@@ -680,9 +692,10 @@ typedef struct st_join_table {
void partial_cleanup();
void add_keyuses_for_splitting();
SplM_plan_info *choose_best_splitting(double record_count,
table_map remaining_tables);
bool fix_splitting(SplM_plan_info *spl_plan, table_map remaining_tables,
SplM_plan_info *choose_best_splitting(uint idx,
table_map remaining_tables,
table_map *spl_pd_boundary);
bool fix_splitting(SplM_plan_info *spl_plan, table_map excluded_tables,
bool is_const_table);
} JOIN_TAB;
......@@ -947,9 +960,21 @@ class POSITION
*/
KEYUSE *key;
/* Cardinality of current partial join ending with this position */
double partial_join_cardinality;
/* Info on splitting plan used at this position */
SplM_plan_info *spl_plan;
/*
If spl_plan is NULL the value of spl_pd_boundary is 0. Otherwise
spl_pd_boundary contains the bitmap of the table from the current
partial join ending at this position that starts the sub-sequence of
tables S from which no conditions are allowed to be used in the plan
spl_plan for the split table joined at this position.
*/
table_map spl_pd_boundary;
/* Cost info for the range filter used at this position */
Range_rowid_filter_cost_info *range_rowid_filter_info;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment