Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit ad425aa

Browse files
committed
Fix ancient thinko in mergejoin cost estimation.
"rescanratio" was computed as 1 + rescanned-tuples / total-inner-tuples, which is sensible if it's to be multiplied by total-inner-tuples or a cost value corresponding to scanning all the inner tuples. But in reality it was (mostly) multiplied by inner_rows or a related cost, numbers that take into account the possibility of stopping short of scanning the whole inner relation thanks to a limited key range in the outer relation. This'd still make sense if we could expect that stopping short would result in a proportional decrease in the number of tuples that have to be rescanned. It does not, however. The argument that establishes the validity of our estimate for that number is independent of whether we scan all of the inner relation or stop short, and experimentation also shows that stopping short doesn't reduce the number of rescanned tuples. So the correct calculation is 1 + rescanned-tuples / inner_rows, and we should be sure to multiply that by inner_rows or a corresponding cost value. Most of the time this doesn't make much difference, but if we have both a high rescan rate (due to lots of duplicate values) and an outer key range much smaller than the inner key range, then the error can be significant, leading to a large underestimate of the cost associated with rescanning. Per report from Vijaykumar Jain. This thinko appears to go all the way back to the introduction of the rescan estimation logic in commit 70fba70, so back-patch to all supported branches. Discussion: https://postgr.es/m/CAE7uO5hMb_TZYJcZmLAgO6iD68AkEK6qCe7i=vZUkCpoKns+EQ@mail.gmail.com
1 parent da1a20a commit ad425aa

File tree

1 file changed

+8
-3
lines changed

1 file changed

+8
-3
lines changed

src/backend/optimizer/path/costsize.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2944,8 +2944,13 @@ final_cost_mergejoin(PlannerInfo *root, MergePath *path,
29442944
if (rescannedtuples < 0)
29452945
rescannedtuples = 0;
29462946
}
2947-
/* We'll inflate various costs this much to account for rescanning */
2948-
rescanratio = 1.0 + (rescannedtuples / inner_path_rows);
2947+
2948+
/*
2949+
* We'll inflate various costs this much to account for rescanning. Note
2950+
* that this is to be multiplied by something involving inner_rows, or
2951+
* another number related to the portion of the inner rel we'll scan.
2952+
*/
2953+
rescanratio = 1.0 + (rescannedtuples / inner_rows);
29492954

29502955
/*
29512956
* Decide whether we want to materialize the inner input to shield it from
@@ -2972,7 +2977,7 @@ final_cost_mergejoin(PlannerInfo *root, MergePath *path,
29722977
* of the generated Material node.
29732978
*/
29742979
mat_inner_cost = inner_run_cost +
2975-
cpu_operator_cost * inner_path_rows * rescanratio;
2980+
cpu_operator_cost * inner_rows * rescanratio;
29762981

29772982
/*
29782983
* If we don't need mark/restore at all, we don't need materialization.

0 commit comments

Comments
 (0)