1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

MDEV-32351 Significant slowdown for query with many outer joins

This patch fixes a performance regression introduced in the patch for the
bug MDEV-21104. The performance regression could affect queries for which
join buffer was used for an outer join such that its on expression from
which a conjunctive condition depended only on outer tables can be
extracted. If the number of records in the join buffer for which this
condition was false greatly exceeded the number of other records the
slowdown could be significant.

If there is a conjunctive condition extracted from the ON expression
depending only on outer tables this condition is evaluated when interesting
fields of each survived record of outer tables are put into the join buffer.
Each such set of fields for any join operation is supplied with a match
flag field used to generate null complemented rows. If the result of the
evaluation of the condition is false the flag is set to MATCH_IMPOSSIBLE.
When looking in the join buffer for records matching a record of the
right operand of the outer join operation the records with such flags
are not needed to be unpacked into record buffers for evaluation of on
expressions.

The patch for MDEV-21104 fixing some problem of wrong results when
'not exists' optimization by mistake broke the code that allowed to
ignore records with the match flag set to MATCH_IMPOSSIBLE when looking
for matching records. As a result such records were unpacked for each
record of the right operand of the outer join operation. This caused
significant execution penalty in some cases.

One of the test cases added in the patch can be used only for demonstration
of the restored performance for the reported query. The second test case is
needed to demonstrate the validity of the fix.
This commit is contained in:
Igor Babaev
2023-10-19 12:09:41 -07:00
committed by Oleksandr Byelkin
parent 11abc21911
commit 954a6decd4
4 changed files with 202 additions and 12 deletions

View File

@ -2054,10 +2054,11 @@ bool JOIN_CACHE::skip_if_matched()
- In the case of a semi-nest the match flag may be in two states
{MATCH_NOT_FOUND, MATCH_FOUND}. The record is skipped if the flag is set
to MATCH_FOUND.
- In the case of a outer join nest when not_exists optimization is applied
the match may be in three states {MATCH_NOT_FOUND, MATCH_IMPOSSIBLE,
MATCH_FOUND. The record is skipped if the flag is set to MATCH_FOUND or
to MATCH_IMPOSSIBLE.
- In the case of an outer join the match may be in three states
{MATCH_NOT_FOUND, MATCH_IMPOSSIBLE, MATCH_FOUND}.
If not_exists optimization is applied the record is skipped when
the flag is set to MATCH_FOUND or to MATCH_IMPOSSIBLE. Otherwise
the record is skipped only when the flag is set to MATCH_IMPOSSIBLE.
If the record is skipped the value of 'pos' is set to point to the position
right after the record.
@ -2080,13 +2081,13 @@ bool JOIN_CACHE::skip_if_not_needed_match()
if (prev_cache)
offset+= prev_cache->get_size_of_rec_offset();
if (!join_tab->check_only_first_match())
return FALSE;
match_fl= get_match_flag_by_pos(pos+offset);
skip= join_tab->first_sj_inner_tab ?
match_fl == MATCH_FOUND : // the case of semi-join
match_fl != MATCH_NOT_FOUND; // the case of outer-join
match_fl == MATCH_FOUND : // the case of semi-join
not_exists_opt_is_applicable &&
join_tab->table->reginfo.not_exists_optimize ?
match_fl != MATCH_NOT_FOUND : // the case of not exist opt
match_fl == MATCH_IMPOSSIBLE;
if (skip)
{
@ -2383,7 +2384,7 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last)
as candidates for matches.
*/
bool not_exists_opt_is_applicable= true;
not_exists_opt_is_applicable= true;
if (check_only_first_match && join_tab->first_inner)
{
/*
@ -2408,8 +2409,9 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last)
}
}
if (!check_only_first_match ||
(join_tab->first_inner && !not_exists_opt_is_applicable) ||
if ((!join_tab->on_precond &&
(!check_only_first_match ||
(join_tab->first_inner && !not_exists_opt_is_applicable))) ||
!skip_next_candidate_for_match(rec_ptr))
{
read_next_candidate_for_match(rec_ptr);