1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

MDEV-31356: Range cost calculations does not take into account join_buffer

This patch also fixes
MDEV-31391 Assertion `((best.records_out) == 0.0 ... failed

Cost changes caused by this change:
- range queries with join buffer now have a notable smaller cost.
- range ranges are bit more expensive as the MULTI_RANGE_COST is now
  properly applied to it in all cases (this extra cost is equal to a
  key lookup).
- table scan cost is slight smaller as we now assume data is cached in
  the engine after the first scan pass. (We did this before for range
  scans and other access methods).
- partition tables had wrong values for max_row_blocks and
  max_index_blocks.  Correcting this, causes range access on
  partitioned tables to have slightly higher cost because of the
  increased estimated IO.
- Using first match + join buffer caused 'filtered' to be calcualted
  wrong.  (Only affected EXPLAIN, not query costs).
- Added cost_without_join_buffer to optimizer_trace.
- check_quick_select() adjusted the number of rows according to persistent
  statistics, but did not adjust cost. Now fixed.

The big change in the patch are:

- In best_access_path(), where we now are using storing the cost in
  'ALL_READ_COST cost' and only converting it to a double at the end.
   This allows us to more exactly calculate the effect of the join_cache.
- In JOIN_TAB::estimate_scan_time(), store the cost also in a
  ALL_READ_COST object.

One of effect if this change is that when joining very small tables:

t1    some_access_method
t2    range
t3    ALL         Use join buffer

This is swiched to

t1      some_access_method
t3      ALL
t2      range      use join buffer

Both plans has the same cost, but as table scan in this case has less
cost than rang, the table scan will be considered first and thus have
precidence.

Test case changes:
- optimizer_trace          - Addition of cost_without_join_buffer
- subselect_mat_cost_bugs  - Small tables and scan versus range
- range & range_mrr_icp    - Range + join_cache is faster than ref
- optimizer_trace          - cost_without_join_buffer, smaller scan cost,
                             range setup cost.
- mrr                      - range+join_buffer used as smaller cost
This commit is contained in:
Monty
2023-05-26 17:26:42 +03:00
parent 7079386587
commit 07b02ab40e
23 changed files with 444 additions and 195 deletions

View File

@@ -2818,7 +2818,7 @@ public:
double comp_cost; /* Cost of comparing found rows with WHERE clause */
double copy_cost; /* Copying the data to 'record' */
double limit_cost; /* Total cost when restricting rows with limit */
double setup_cost; /* MULTI_RANGE_READ_SETUP_COST or similar */
IO_AND_CPU_COST index_cost;
IO_AND_CPU_COST row_cost;
@@ -2835,8 +2835,8 @@ public:
double total_cost() const
{
return ((index_cost.io + row_cost.io) * avg_io_cost+
index_cost.cpu + row_cost.cpu + comp_cost + copy_cost +
cpu_cost);
index_cost.cpu + row_cost.cpu + copy_cost +
comp_cost + cpu_cost + setup_cost);
}
/* Cost for just fetching and copying a row (no compare costs) */
@@ -2881,6 +2881,7 @@ public:
copy_cost+= cost->copy_cost;
comp_cost+= cost->comp_cost;
cpu_cost+= cost->cpu_cost;
setup_cost+= cost->setup_cost;
}
inline void reset()
@@ -2888,6 +2889,7 @@ public:
avg_io_cost= 0;
comp_cost= cpu_cost= 0.0;
copy_cost= limit_cost= 0.0;
setup_cost= 0.0;
index_cost= {0,0};
row_cost= {0,0};
}
@@ -3333,10 +3335,11 @@ private:
For non partitioned handlers this is &TABLE_SHARE::ha_share.
*/
Handler_share **ha_share;
public:
double optimizer_where_cost; // Copy of THD->...optimzer_where_cost
double optimizer_scan_setup_cost; // Copy of THD->...optimzer_scan_...
public:
handler(handlerton *ht_arg, TABLE_SHARE *share_arg)
:table_share(share_arg), table(0),
estimation_rows_to_insert(0),
@@ -3603,6 +3606,18 @@ public:
return ((cost->index_cost.cpu + cost->row_cost.cpu + cost->copy_cost) +
blocks * DISK_READ_COST * DISK_READ_RATIO);
}
/*
Same as above but without capping.
This is only used for comparing cost with s->quick_read time, which
does not do any capping.
*/
inline double cost_no_capping(ALL_READ_COST *cost)
{
double blocks= (cost->index_cost.io + cost->row_cost.io);
return ((cost->index_cost.cpu + cost->row_cost.cpu + cost->copy_cost) +
blocks * DISK_READ_COST * DISK_READ_RATIO);
}
/*
Calculate cost when we are going to excute the given read method
@@ -3621,7 +3636,7 @@ public:
blocks * DISK_READ_COST * DISK_READ_RATIO);
}
inline ulonglong row_blocks()
virtual ulonglong row_blocks()
{
return (stats.data_file_length + IO_SIZE-1) / IO_SIZE;
}