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

Make the most important optimizer constants user variables

Variables added:
- optimizer_index_block_copy_cost
- optimizer_key_copy_cost
- optimizer_key_next_find_cost
- optimizer_key_compare_cost
- optimizer_row_copy_cost
- optimizer_where_compare_cost

Some rename of defines was done to make the internal defines similar to
the visible ones:
TIME_FOR_COMPARE -> WHERE_COST; WHERE_COST was also "inverted" to be
a number between 0 and 1 that is multiply with accepted records
(similar to other optimizer variables).
TIME_FOR_COMPARE_IDX -> KEY_COMPARE_COST. This is also inverted,
similar to TIME_FOR_COMPARE.
TIME_FOR_COMPARE_ROWID -> ROWID_COMPARE_COST. This is also inverted,
similar to TIME_FOR_COMPARE.

All default costs are identical to what they where before this patch.

Other things:
- Compare factor in get_merge_buffers_cost() was inverted.
- Changed namespace to static in filesort_utils.cc
This commit is contained in:
Monty
2022-04-05 20:12:29 +03:00
committed by Sergei Petrunia
parent b6215b9b20
commit 5e651c9aea
28 changed files with 451 additions and 143 deletions

View File

@@ -80,7 +80,7 @@ EXPLAIN
SELECT * FROM City SELECT * FROM City
WHERE Name LIKE 'M%' AND Population > 7000000; WHERE Name LIKE 'M%' AND Population > 7000000;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE City index_merge Population,Name Population,Name 4,35 NULL # Using sort_intersect(Population,Name); Using where 1 SIMPLE City range Population,Name Population 4 NULL # Using index condition; Using where
SELECT * FROM City USE INDEX () SELECT * FROM City USE INDEX ()
WHERE Name LIKE 'C%' AND Population > 1000000; WHERE Name LIKE 'C%' AND Population > 1000000;
ID Name Country Population ID Name Country Population
@@ -335,8 +335,8 @@ ID Name Country Population
SELECT * FROM City SELECT * FROM City
WHERE Name LIKE 'M%' AND Population > 7000000; WHERE Name LIKE 'M%' AND Population > 7000000;
ID Name Country Population ID Name Country Population
1024 Mumbai (Bombay) IND 10500000
3580 Moscow RUS 8389200 3580 Moscow RUS 8389200
1024 Mumbai (Bombay) IND 10500000
SELECT COUNT(*) FROM City WHERE Name BETWEEN 'M' AND 'N'; SELECT COUNT(*) FROM City WHERE Name BETWEEN 'M' AND 'N';
COUNT(*) COUNT(*)
301 301
@@ -372,7 +372,7 @@ EXPLAIN
SELECT * FROM City SELECT * FROM City
WHERE Name BETWEEN 'G' AND 'K' AND Population > 500000 AND Country LIKE 'C%'; WHERE Name BETWEEN 'G' AND 'K' AND Population > 500000 AND Country LIKE 'C%';
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE City index_merge Population,Name,Country Name,Country,Population # NULL # Using sort_intersect(Name,Country,Population); Using where 1 SIMPLE City index_merge Population,Name,Country Name,Country # NULL # Using sort_intersect(Name,Country); Using where
SELECT * FROM City USE INDEX () SELECT * FROM City USE INDEX ()
WHERE Name BETWEEN 'M' AND 'N' AND Population > 1000000 AND Country LIKE 'C%'; WHERE Name BETWEEN 'M' AND 'N' AND Population > 1000000 AND Country LIKE 'C%';
ID Name Country Population ID Name Country Population
@@ -463,12 +463,12 @@ EXPLAIN
SELECT * FROM City SELECT * FROM City
WHERE ID BETWEEN 501 AND 1000 AND Population > 700000 AND Country LIKE 'C%'; WHERE ID BETWEEN 501 AND 1000 AND Population > 700000 AND Country LIKE 'C%';
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE City index_merge PRIMARY,Population,Country Population,PRIMARY,Country 4,4,3 NULL # Using sort_intersect(Population,PRIMARY,Country); Using where 1 SIMPLE City index_merge PRIMARY,Population,Country Population,PRIMARY 4,4 NULL # Using sort_intersect(Population,PRIMARY); Using where
EXPLAIN EXPLAIN
SELECT * FROM City SELECT * FROM City
WHERE ID BETWEEN 1 AND 500 AND Population > 700000 AND Country LIKE 'C%'; WHERE ID BETWEEN 1 AND 500 AND Population > 700000 AND Country LIKE 'C%';
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE City index_merge PRIMARY,Population,Country Population,PRIMARY,Country 4,4,3 NULL # Using sort_intersect(Population,PRIMARY,Country); Using where 1 SIMPLE City index_merge PRIMARY,Population,Country Population,PRIMARY 4,4 NULL # Using sort_intersect(Population,PRIMARY); Using where
EXPLAIN EXPLAIN
SELECT * FROM City SELECT * FROM City
WHERE ID BETWEEN 2001 AND 2500 AND Population > 300000 AND Country LIKE 'H%'; WHERE ID BETWEEN 2001 AND 2500 AND Population > 300000 AND Country LIKE 'H%';

View File

@@ -469,12 +469,12 @@ EXPLAIN
SELECT * FROM City SELECT * FROM City
WHERE ID BETWEEN 501 AND 1000 AND Population > 700000 AND Country LIKE 'C%'; WHERE ID BETWEEN 501 AND 1000 AND Population > 700000 AND Country LIKE 'C%';
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE City index_merge PRIMARY,Population,Country PRIMARY,Population,Country 4,4,7 NULL # Using sort_intersect(PRIMARY,Population,Country); Using where 1 SIMPLE City index_merge PRIMARY,Population,Country PRIMARY,Population 4,4 NULL # Using sort_intersect(PRIMARY,Population); Using where
EXPLAIN EXPLAIN
SELECT * FROM City SELECT * FROM City
WHERE ID BETWEEN 1 AND 500 AND Population > 700000 AND Country LIKE 'C%'; WHERE ID BETWEEN 1 AND 500 AND Population > 700000 AND Country LIKE 'C%';
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE City index_merge PRIMARY,Population,Country PRIMARY,Population,Country 4,4,7 NULL # Using sort_intersect(PRIMARY,Population,Country); Using where 1 SIMPLE City index_merge PRIMARY,Population,Country PRIMARY,Population 4,4 NULL # Using sort_intersect(PRIMARY,Population); Using where
EXPLAIN EXPLAIN
SELECT * FROM City SELECT * FROM City
WHERE ID BETWEEN 2001 AND 2500 AND Population > 300000 AND Country LIKE 'H%'; WHERE ID BETWEEN 2001 AND 2500 AND Population > 300000 AND Country LIKE 'H%';
@@ -718,12 +718,12 @@ EXPLAIN
SELECT * FROM City SELECT * FROM City
WHERE Name BETWEEN 'G' AND 'J' AND Population > 500000 AND Country LIKE 'C%'; WHERE Name BETWEEN 'G' AND 'J' AND Population > 500000 AND Country LIKE 'C%';
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE City index_merge Population,Country,Name Name,Population,Country 35,4,3 NULL # Using sort_intersect(Name,Population,Country); Using where 1 SIMPLE City index_merge Population,Country,Name Name,Population 35,4 NULL # Using sort_intersect(Name,Population); Using where
EXPLAIN EXPLAIN
SELECT * FROM City SELECT * FROM City
WHERE ID BETWEEN 1 AND 500 AND Population > 700000 AND Country LIKE 'C%'; WHERE ID BETWEEN 1 AND 500 AND Population > 700000 AND Country LIKE 'C%';
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE City index_merge PRIMARY,Population,Country PRIMARY,Population,Country 4,4,7 NULL # Using sort_intersect(PRIMARY,Population,Country); Using where 1 SIMPLE City index_merge PRIMARY,Population,Country PRIMARY,Population 4,4 NULL # Using sort_intersect(PRIMARY,Population); Using where
EXPLAIN EXPLAIN
SELECT * FROM City SELECT * FROM City
WHERE ID BETWEEN 3001 AND 4000 AND Population > 600000 WHERE ID BETWEEN 3001 AND 4000 AND Population > 600000

View File

@@ -724,11 +724,22 @@ The following specify which files/extra groups are read (specified before remain
Expected hit rate of the row and index cache in storage Expected hit rate of the row and index cache in storage
engines. The value should be an integer between 0 and 99, engines. The value should be an integer between 0 and 99,
where 0 means cache is empty and 99 means that value is where 0 means cache is empty and 99 means that value is
almost always in the cache almost always in the cache.
--optimizer-extra-pruning-depth=# --optimizer-extra-pruning-depth=#
If the optimizer needs to enumerate join prefix of this If the optimizer needs to enumerate join prefix of this
size or larger, then it will try agressively prune away size or larger, then it will try agressively prune away
the search space. the search space.
--optimizer-index-block-copy-cost=#
Cost of copying a key block from the cache to intern
storage as part of an index scan.
--optimizer-key-compare-cost=#
Cost of checking a key against the end key condition.
--optimizer-key-copy-cost=#
Cost of finding the next key in the engine and copying it
to the SQL layer.
--optimizer-key-next-find-cost=#
Cost of finding the next key and rowid when using
filters.
--optimizer-max-sel-arg-weight=# --optimizer-max-sel-arg-weight=#
The maximum weight of the SEL_ARG graph. Set to 0 for no The maximum weight of the SEL_ARG graph. Set to 0 for no
limit limit
@@ -739,6 +750,9 @@ The following specify which files/extra groups are read (specified before remain
heuristic, thus perform exhaustive search: 1 - prune heuristic, thus perform exhaustive search: 1 - prune
plans based on cost and number of retrieved rows eq_ref: plans based on cost and number of retrieved rows eq_ref:
2 - prune also if we find an eq_ref chain 2 - prune also if we find an eq_ref chain
--optimizer-row-copy-cost=#
Cost of copying a row from the engine or the join cache
to the SQL layer.
--optimizer-search-depth=# --optimizer-search-depth=#
Maximum depth of search performed by the query optimizer. Maximum depth of search performed by the query optimizer.
Values larger than the number of relations in a query Values larger than the number of relations in a query
@@ -791,6 +805,8 @@ The following specify which files/extra groups are read (specified before remain
the cardinality of a partial join.5 - additionally use the cardinality of a partial join.5 - additionally use
selectivity of certain non-range predicates calculated on selectivity of certain non-range predicates calculated on
record samples record samples
--optimizer-where-cost=#
Cost of checking the row against the WHERE clause.
--performance-schema --performance-schema
Enable the performance schema. Enable the performance schema.
--performance-schema-accounts-size=# --performance-schema-accounts-size=#
@@ -1705,14 +1721,20 @@ old-passwords FALSE
old-style-user-limits FALSE old-style-user-limits FALSE
optimizer-cache-hit-ratio 50 optimizer-cache-hit-ratio 50
optimizer-extra-pruning-depth 8 optimizer-extra-pruning-depth 8
optimizer-index-block-copy-cost 0.2
optimizer-key-compare-cost 0.05
optimizer-key-copy-cost 0.025
optimizer-key-next-find-cost 0.0125
optimizer-max-sel-arg-weight 32000 optimizer-max-sel-arg-weight 32000
optimizer-prune-level 2 optimizer-prune-level 2
optimizer-row-copy-cost 0.05
optimizer-search-depth 62 optimizer-search-depth 62
optimizer-selectivity-sampling-limit 100 optimizer-selectivity-sampling-limit 100
optimizer-switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on optimizer-switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on
optimizer-trace optimizer-trace
optimizer-trace-max-mem-size 1048576 optimizer-trace-max-mem-size 1048576
optimizer-use-condition-selectivity 4 optimizer-use-condition-selectivity 4
optimizer-where-cost 0.2
performance-schema FALSE performance-schema FALSE
performance-schema-accounts-size -1 performance-schema-accounts-size -1
performance-schema-consumer-events-stages-current FALSE performance-schema-consumer-events-stages-current FALSE

View File

@@ -29,6 +29,7 @@ perl;
collation-server character-set-server log-tc-size table-cache collation-server character-set-server log-tc-size table-cache
table-open-cache table-open-cache-instances max-connections table-open-cache table-open-cache-instances max-connections
tls-version version.* password-reuse-check tls-version version.* password-reuse-check
provider-bzip2 provider-lzma provider-lzo
password-reuse-check-interval/; password-reuse-check-interval/;
# Plugins which may or may not be there: # Plugins which may or may not be there:

View File

@@ -158,7 +158,7 @@ explain select * from t1 where a=1 or b=1 {
} }
} }
], ],
"index_roworder_union_cost": 2.595171589, "index_roworder_union_cost": 2.601171589,
"members": 2, "members": 2,
"chosen": true "chosen": true
} }
@@ -187,7 +187,7 @@ explain select * from t1 where a=1 or b=1 {
] ]
}, },
"rows_for_plan": 2, "rows_for_plan": 2,
"cost_for_plan": 2.595171589, "cost_for_plan": 2.601171589,
"chosen": true "chosen": true
} }
} }
@@ -220,14 +220,14 @@ explain select * from t1 where a=1 or b=1 {
"rows": 2, "rows": 2,
"rows_after_scan": 2, "rows_after_scan": 2,
"rows_after_filter": 2, "rows_after_filter": 2,
"cost": 2.595171589, "cost": 2.601171589,
"chosen": true "chosen": true
} }
], ],
"chosen_access_method": { "chosen_access_method": {
"type": "index_merge", "type": "index_merge",
"records": 2, "records": 2,
"cost": 2.595171589, "cost": 2.601171589,
"uses_join_buffering": false "uses_join_buffering": false
} }
} }
@@ -238,14 +238,14 @@ explain select * from t1 where a=1 or b=1 {
"plan_prefix": [], "plan_prefix": [],
"table": "t1", "table": "t1",
"rows_for_plan": 2, "rows_for_plan": 2,
"cost_for_plan": 2.595171589 "cost_for_plan": 2.601171589
} }
] ]
}, },
{ {
"best_join_order": ["t1"], "best_join_order": ["t1"],
"rows": 2, "rows": 2,
"cost": 2.595171589 "cost": 2.601171589
}, },
{ {
"substitute_best_equal": { "substitute_best_equal": {
@@ -630,7 +630,7 @@ JSON_DETAILED(JSON_EXTRACT(trace, '$**.analyzing_range_alternatives'))
} }
} }
], ],
"index_roworder_union_cost": 332.5637481, "index_roworder_union_cost": 333.0257481,
"members": 2, "members": 2,
"chosen": true "chosen": true
} }
@@ -697,7 +697,7 @@ JSON_DETAILED(JSON_EXTRACT(trace, '$**.chosen_range_access_summary'))
] ]
}, },
"rows_for_plan": 154, "rows_for_plan": 154,
"cost_for_plan": 332.5637481, "cost_for_plan": 333.0257481,
"chosen": true "chosen": true
} }
] ]

View File

@@ -88,7 +88,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
2 DEPENDENT SUBQUERY region eq_ref PRIMARY PRIMARY 4 dbt3_s001.nation.n_regionkey 1 20.00 Using where 2 DEPENDENT SUBQUERY region eq_ref PRIMARY PRIMARY 4 dbt3_s001.nation.n_regionkey 1 20.00 Using where
Warnings: Warnings:
Note 1276 Field or reference 'dbt3_s001.part.p_partkey' of SELECT #2 was resolved in SELECT #1 Note 1276 Field or reference 'dbt3_s001.part.p_partkey' of SELECT #2 was resolved in SELECT #1
Note 1003 /* select#1 */ select `dbt3_s001`.`supplier`.`s_acctbal` AS `s_acctbal`,`dbt3_s001`.`supplier`.`s_name` AS `s_name`,`dbt3_s001`.`nation`.`n_name` AS `n_name`,`dbt3_s001`.`part`.`p_partkey` AS `p_partkey`,`dbt3_s001`.`part`.`p_mfgr` AS `p_mfgr`,`dbt3_s001`.`supplier`.`s_address` AS `s_address`,`dbt3_s001`.`supplier`.`s_phone` AS `s_phone`,`dbt3_s001`.`supplier`.`s_comment` AS `s_comment` from `dbt3_s001`.`part` join `dbt3_s001`.`supplier` join `dbt3_s001`.`partsupp` join `dbt3_s001`.`nation` join `dbt3_s001`.`region` where `dbt3_s001`.`partsupp`.`ps_partkey` = `dbt3_s001`.`part`.`p_partkey` and `dbt3_s001`.`supplier`.`s_suppkey` = `dbt3_s001`.`partsupp`.`ps_suppkey` and `dbt3_s001`.`nation`.`n_nationkey` = `dbt3_s001`.`supplier`.`s_nationkey` and `dbt3_s001`.`nation`.`n_regionkey` = `dbt3_s001`.`region`.`r_regionkey` and `dbt3_s001`.`region`.`r_name` = 'ASIA' and (`dbt3_s001`.`part`.`p_size` = 9 or `dbt3_s001`.`part`.`p_size` = 19999) and `dbt3_s001`.`part`.`p_type` like '%TIN' and `dbt3_s001`.`partsupp`.`ps_supplycost` = <expr_cache><`dbt3_s001`.`part`.`p_partkey`>((/* select#2 */ select min(`dbt3_s001`.`partsupp`.`ps_supplycost`) from `dbt3_s001`.`partsupp` join `dbt3_s001`.`supplier` join `dbt3_s001`.`nation` join `dbt3_s001`.`region` where `dbt3_s001`.`supplier`.`s_suppkey` = `dbt3_s001`.`partsupp`.`ps_suppkey` and `dbt3_s001`.`nation`.`n_nationkey` = `dbt3_s001`.`supplier`.`s_nationkey` and `dbt3_s001`.`nation`.`n_regionkey` = `dbt3_s001`.`region`.`r_regionkey` and `dbt3_s001`.`region`.`r_name` = 'ASIA' and `dbt3_s001`.`part`.`p_partkey` = `dbt3_s001`.`partsupp`.`ps_partkey`)) order by `dbt3_s001`.`supplier`.`s_acctbal` desc,`dbt3_s001`.`nation`.`n_name`,`dbt3_s001`.`supplier`.`s_name`,`dbt3_s001`.`part`.`p_partkey` Note 1003 /* select#1 */ select `dbt3_s001`.`supplier`.`s_acctbal` AS `s_acctbal`,`dbt3_s001`.`supplier`.`s_name` AS `s_name`,`dbt3_s001`.`nation`.`n_name` AS `n_name`,`dbt3_s001`.`part`.`p_partkey` AS `p_partkey`,`dbt3_s001`.`part`.`p_mfgr` AS `p_mfgr`,`dbt3_s001`.`supplier`.`s_address` AS `s_address`,`dbt3_s001`.`supplier`.`s_phone` AS `s_phone`,`dbt3_s001`.`supplier`.`s_comment` AS `s_comment` from `dbt3_s001`.`part` join `dbt3_s001`.`supplier` join `dbt3_s001`.`partsupp` join `dbt3_s001`.`nation` join `dbt3_s001`.`region` where `dbt3_s001`.`partsupp`.`ps_partkey` = `dbt3_s001`.`part`.`p_partkey` and `dbt3_s001`.`supplier`.`s_suppkey` = `dbt3_s001`.`partsupp`.`ps_suppkey` and `dbt3_s001`.`nation`.`n_nationkey` = `dbt3_s001`.`supplier`.`s_nationkey` and `dbt3_s001`.`nation`.`n_regionkey` = `dbt3_s001`.`region`.`r_regionkey` and `dbt3_s001`.`region`.`r_name` = 'ASIA' and (`dbt3_s001`.`part`.`p_size` = 9 or `dbt3_s001`.`part`.`p_size` = 19999) and `dbt3_s001`.`part`.`p_type` like '%TIN' and `dbt3_s001`.`partsupp`.`ps_supplycost` = <expr_cache><`dbt3_s001`.`part`.`p_partkey`>((/* select#2 */ select min(`dbt3_s001`.`partsupp`.`ps_supplycost`) from `dbt3_s001`.`partsupp` join `dbt3_s001`.`supplier` join `dbt3_s001`.`nation` join `dbt3_s001`.`region` where `dbt3_s001`.`supplier`.`s_suppkey` = `dbt3_s001`.`partsupp`.`ps_suppkey` and `dbt3_s001`.`nation`.`n_nationkey` = `dbt3_s001`.`supplier`.`s_nationkey` and `dbt3_s001`.`region`.`r_regionkey` = `dbt3_s001`.`nation`.`n_regionkey` and `dbt3_s001`.`region`.`r_name` = 'ASIA' and `dbt3_s001`.`part`.`p_partkey` = `dbt3_s001`.`partsupp`.`ps_partkey`)) order by `dbt3_s001`.`supplier`.`s_acctbal` desc,`dbt3_s001`.`nation`.`n_name`,`dbt3_s001`.`supplier`.`s_name`,`dbt3_s001`.`part`.`p_partkey`
set optimizer_use_condition_selectivity=4; set optimizer_use_condition_selectivity=4;
explain extended explain extended
select select

View File

@@ -2292,6 +2292,16 @@ NUMERIC_BLOCK_SIZE 1
ENUM_VALUE_LIST NULL ENUM_VALUE_LIST NULL
READ_ONLY YES READ_ONLY YES
COMMAND_LINE_ARGUMENT REQUIRED COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_CACHE_HIT_RATIO
VARIABLE_SCOPE SESSION
VARIABLE_TYPE INT UNSIGNED
VARIABLE_COMMENT Expected hit rate of the row and index cache in storage engines. The value should be an integer between 0 and 99, where 0 means cache is empty and 99 means that value is almost always in the cache.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 99
NUMERIC_BLOCK_SIZE 1
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_EXTRA_PRUNING_DEPTH VARIABLE_NAME OPTIMIZER_EXTRA_PRUNING_DEPTH
VARIABLE_SCOPE SESSION VARIABLE_SCOPE SESSION
VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_TYPE BIGINT UNSIGNED
@@ -2302,6 +2312,46 @@ NUMERIC_BLOCK_SIZE 1
ENUM_VALUE_LIST NULL ENUM_VALUE_LIST NULL
READ_ONLY NO READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_INDEX_BLOCK_COPY_COST
VARIABLE_SCOPE SESSION
VARIABLE_TYPE DOUBLE
VARIABLE_COMMENT Cost of copying a key block from the cache to intern storage as part of an index scan.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 1
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_KEY_COMPARE_COST
VARIABLE_SCOPE SESSION
VARIABLE_TYPE DOUBLE
VARIABLE_COMMENT Cost of checking a key against the end key condition.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 1
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_KEY_COPY_COST
VARIABLE_SCOPE SESSION
VARIABLE_TYPE DOUBLE
VARIABLE_COMMENT Cost of finding the next key in the engine and copying it to the SQL layer.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 1
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_KEY_NEXT_FIND_COST
VARIABLE_SCOPE SESSION
VARIABLE_TYPE DOUBLE
VARIABLE_COMMENT Cost of finding the next key and rowid when using filters.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 1
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_MAX_SEL_ARG_WEIGHT VARIABLE_NAME OPTIMIZER_MAX_SEL_ARG_WEIGHT
VARIABLE_SCOPE SESSION VARIABLE_SCOPE SESSION
VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_TYPE BIGINT UNSIGNED
@@ -2322,6 +2372,16 @@ NUMERIC_BLOCK_SIZE 1
ENUM_VALUE_LIST NULL ENUM_VALUE_LIST NULL
READ_ONLY NO READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_ROW_COPY_COST
VARIABLE_SCOPE SESSION
VARIABLE_TYPE DOUBLE
VARIABLE_COMMENT Cost of copying a row from the engine or the join cache to the SQL layer.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 1
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_SEARCH_DEPTH VARIABLE_NAME OPTIMIZER_SEARCH_DEPTH
VARIABLE_SCOPE SESSION VARIABLE_SCOPE SESSION
VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_TYPE BIGINT UNSIGNED
@@ -2382,6 +2442,16 @@ NUMERIC_BLOCK_SIZE 1
ENUM_VALUE_LIST NULL ENUM_VALUE_LIST NULL
READ_ONLY NO READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_WHERE_COST
VARIABLE_SCOPE SESSION
VARIABLE_TYPE DOUBLE
VARIABLE_COMMENT Cost of checking the row against the WHERE clause.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 1
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME PERFORMANCE_SCHEMA VARIABLE_NAME PERFORMANCE_SCHEMA
VARIABLE_SCOPE GLOBAL VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE BOOLEAN VARIABLE_TYPE BOOLEAN

View File

@@ -2465,7 +2465,7 @@ COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_CACHE_HIT_RATIO VARIABLE_NAME OPTIMIZER_CACHE_HIT_RATIO
VARIABLE_SCOPE SESSION VARIABLE_SCOPE SESSION
VARIABLE_TYPE INT UNSIGNED VARIABLE_TYPE INT UNSIGNED
VARIABLE_COMMENT Expected hit rate of the row and index cache in storage engines. The value should be an integer between 0 and 99, where 0 means cache is empty and 99 means that value is almost always in the cache VARIABLE_COMMENT Expected hit rate of the row and index cache in storage engines. The value should be an integer between 0 and 99, where 0 means cache is empty and 99 means that value is almost always in the cache.
NUMERIC_MIN_VALUE 0 NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 99 NUMERIC_MAX_VALUE 99
NUMERIC_BLOCK_SIZE 1 NUMERIC_BLOCK_SIZE 1
@@ -2482,6 +2482,46 @@ NUMERIC_BLOCK_SIZE 1
ENUM_VALUE_LIST NULL ENUM_VALUE_LIST NULL
READ_ONLY NO READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_INDEX_BLOCK_COPY_COST
VARIABLE_SCOPE SESSION
VARIABLE_TYPE DOUBLE
VARIABLE_COMMENT Cost of copying a key block from the cache to intern storage as part of an index scan.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 1
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_KEY_COMPARE_COST
VARIABLE_SCOPE SESSION
VARIABLE_TYPE DOUBLE
VARIABLE_COMMENT Cost of checking a key against the end key condition.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 1
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_KEY_COPY_COST
VARIABLE_SCOPE SESSION
VARIABLE_TYPE DOUBLE
VARIABLE_COMMENT Cost of finding the next key in the engine and copying it to the SQL layer.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 1
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_KEY_NEXT_FIND_COST
VARIABLE_SCOPE SESSION
VARIABLE_TYPE DOUBLE
VARIABLE_COMMENT Cost of finding the next key and rowid when using filters.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 1
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_MAX_SEL_ARG_WEIGHT VARIABLE_NAME OPTIMIZER_MAX_SEL_ARG_WEIGHT
VARIABLE_SCOPE SESSION VARIABLE_SCOPE SESSION
VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_TYPE BIGINT UNSIGNED
@@ -2502,6 +2542,16 @@ NUMERIC_BLOCK_SIZE 1
ENUM_VALUE_LIST NULL ENUM_VALUE_LIST NULL
READ_ONLY NO READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_ROW_COPY_COST
VARIABLE_SCOPE SESSION
VARIABLE_TYPE DOUBLE
VARIABLE_COMMENT Cost of copying a row from the engine or the join cache to the SQL layer.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 1
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_SEARCH_DEPTH VARIABLE_NAME OPTIMIZER_SEARCH_DEPTH
VARIABLE_SCOPE SESSION VARIABLE_SCOPE SESSION
VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_TYPE BIGINT UNSIGNED
@@ -2562,6 +2612,16 @@ NUMERIC_BLOCK_SIZE 1
ENUM_VALUE_LIST NULL ENUM_VALUE_LIST NULL
READ_ONLY NO READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_WHERE_COST
VARIABLE_SCOPE SESSION
VARIABLE_TYPE DOUBLE
VARIABLE_COMMENT Cost of checking the row against the WHERE clause.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 1
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME PERFORMANCE_SCHEMA VARIABLE_NAME PERFORMANCE_SCHEMA
VARIABLE_SCOPE GLOBAL VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE BOOLEAN VARIABLE_TYPE BOOLEAN

View File

@@ -1594,17 +1594,18 @@ static bool check_if_pq_applicable(Sort_param *param,
scanning the table and comparing the rows with the where clause! scanning the table and comparing the rows with the where clause!
*/ */
const double sort_merge_cost= const double sort_merge_cost=
get_merge_many_buffs_cost_fast(num_rows, get_merge_many_buffs_cost_fast(table->in_use, num_rows,
num_available_keys, num_available_keys,
(uint)row_length); (uint)row_length);
/* /*
PQ has cost: PQ has cost:
(insert + qsort) * log(queue size) / TIME_FOR_COMPARE_ROWID + (insert + qsort) * log(queue size) * ROWID_COMPARE_COST
cost of rowid lookup of the original row to find the addon fields. cost of rowid lookup of the original row to find the addon fields.
*/ */
const double pq_cpu_cost= const double pq_cpu_cost=
(PQ_slowness * num_rows + param->max_keys_per_buffer) * (PQ_slowness * num_rows + param->max_keys_per_buffer) *
log((double) param->max_keys_per_buffer) / TIME_FOR_COMPARE_ROWID; log((double) param->max_keys_per_buffer) *
ROWID_COMPARE_COST_THD(table->in_use);
const double pq_io_cost= table->file->ha_rnd_pos_time(param->max_rows); const double pq_io_cost= table->file->ha_rnd_pos_time(param->max_rows);
const double pq_cost= pq_cpu_cost + pq_io_cost; const double pq_cost= pq_cpu_cost + pq_io_cost;

View File

@@ -23,17 +23,16 @@
PSI_memory_key key_memory_Filesort_buffer_sort_keys; PSI_memory_key key_memory_Filesort_buffer_sort_keys;
namespace {
/** /**
A local helper function. See comments for get_merge_buffers_cost(). A local helper function. See comments for get_merge_buffers_cost().
*/ */
double get_merge_cost(ha_rows num_elements, ha_rows num_buffers, uint elem_size)
static double get_merge_cost(double rowid_compare_cost, ha_rows num_elements,
ha_rows num_buffers, uint elem_size)
{ {
return return (2.0 * ((double) num_elements * elem_size) / IO_SIZE
2.0 * ((double) num_elements * elem_size) / IO_SIZE + (double) num_elements * log((double) num_buffers) *
+ (double) num_elements * log((double) num_buffers) / rowid_compare_cost / M_LN2);
(TIME_FOR_COMPARE_ROWID * M_LN2);
}
} }
/** /**
@@ -43,19 +42,20 @@ double get_merge_cost(ha_rows num_elements, ha_rows num_buffers, uint elem_size)
see comments for get_merge_buffers_cost(). see comments for get_merge_buffers_cost().
TODO: Use this function for Unique::get_use_cost(). TODO: Use this function for Unique::get_use_cost().
*/ */
double get_merge_many_buffs_cost_fast(ha_rows num_rows,
double get_merge_many_buffs_cost_fast(THD *thd,
ha_rows num_rows,
ha_rows num_keys_per_buffer, ha_rows num_keys_per_buffer,
uint elem_size) uint elem_size)
{ {
ha_rows num_buffers= num_rows / num_keys_per_buffer; ha_rows num_buffers= num_rows / num_keys_per_buffer;
ha_rows last_n_elems= num_rows % num_keys_per_buffer; ha_rows last_n_elems= num_rows % num_keys_per_buffer;
double total_cost; double total_cost, rowid_compare_cost= ROWID_COMPARE_COST_THD(thd);
// Calculate CPU cost of sorting buffers. // Calculate CPU cost of sorting buffers.
total_cost= total_cost=
((num_buffers * num_keys_per_buffer * log(1.0 + num_keys_per_buffer) + ((num_buffers * num_keys_per_buffer * log(1.0 + num_keys_per_buffer) +
last_n_elems * log(1.0 + last_n_elems)) / last_n_elems * log(1.0 + last_n_elems)) * rowid_compare_cost);
TIME_FOR_COMPARE_ROWID);
// Simulate behavior of merge_many_buff(). // Simulate behavior of merge_many_buff().
while (num_buffers >= MERGEBUFF2) while (num_buffers >= MERGEBUFF2)
@@ -69,14 +69,16 @@ double get_merge_many_buffs_cost_fast(ha_rows num_rows,
// Cost of merge sort 'num_merge_calls'. // Cost of merge sort 'num_merge_calls'.
total_cost+= total_cost+=
num_merge_calls * num_merge_calls *
get_merge_cost(num_keys_per_buffer * MERGEBUFF, MERGEBUFF, elem_size); get_merge_cost(rowid_compare_cost, num_keys_per_buffer * MERGEBUFF,
MERGEBUFF, elem_size);
// # of records in remaining buffers. // # of records in remaining buffers.
last_n_elems+= num_remaining_buffs * num_keys_per_buffer; last_n_elems+= num_remaining_buffs * num_keys_per_buffer;
// Cost of merge sort of remaining buffers. // Cost of merge sort of remaining buffers.
total_cost+= total_cost+=
get_merge_cost(last_n_elems, 1 + num_remaining_buffs, elem_size); get_merge_cost(rowid_compare_cost, last_n_elems, 1 + num_remaining_buffs,
elem_size);
num_buffers= num_merge_calls; num_buffers= num_merge_calls;
num_keys_per_buffer*= MERGEBUFF; num_keys_per_buffer*= MERGEBUFF;
@@ -84,7 +86,8 @@ double get_merge_many_buffs_cost_fast(ha_rows num_rows,
// Simulate final merge_buff call. // Simulate final merge_buff call.
last_n_elems+= num_keys_per_buffer * num_buffers; last_n_elems+= num_keys_per_buffer * num_buffers;
total_cost+= get_merge_cost(last_n_elems, 1 + num_buffers, elem_size); total_cost+= get_merge_cost(rowid_compare_cost, last_n_elems, 1 + num_buffers,
elem_size);
return total_cost; return total_cost;
} }

View File

@@ -39,7 +39,7 @@ class Sort_param;
See also comments get_merge_many_buffs_cost(). See also comments get_merge_many_buffs_cost().
*/ */
double get_merge_many_buffs_cost_fast(ha_rows num_rows, double get_merge_many_buffs_cost_fast(THD *thd, ha_rows num_rows,
ha_rows num_keys_per_buffer, ha_rows num_keys_per_buffer,
uint elem_size); uint elem_size);

View File

@@ -12145,6 +12145,15 @@ ha_partition::can_convert_nocopy(const Field &field,
return true; return true;
} }
void ha_partition::set_optimizer_costs(THD *thd)
{
handler::set_optimizer_costs(thd);
for (uint i= bitmap_get_first_set(&m_part_info->read_partitions);
i < m_tot_parts;
i= bitmap_get_next_set(&m_part_info->read_partitions, i))
m_file[i]->set_optimizer_costs(thd);
}
struct st_mysql_storage_engine partition_storage_engine= struct st_mysql_storage_engine partition_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION }; { MYSQL_HANDLERTON_INTERFACE_VERSION };

View File

@@ -1637,5 +1637,6 @@ public:
bool can_convert_nocopy(const Field &field, bool can_convert_nocopy(const Field &field,
const Column_definition &new_field) const override; const Column_definition &new_field) const override;
void set_optimizer_costs(THD *thd) override;
}; };
#endif /* HA_PARTITION_INCLUDED */ #endif /* HA_PARTITION_INCLUDED */

View File

@@ -3249,7 +3249,7 @@ LEX_CSTRING *handler::engine_name()
those key entries into the engine buffers. those key entries into the engine buffers.
This function doesn't take in account into copying the key to record This function doesn't take in account into copying the key to record
(KEY_COPY_COST) or comparing the key to the where clause (TIME_FOR_COMPARE) (KEY_COPY_COST) or comparing the key to the where clause (WHERE_COST)
*/ */
double handler::keyread_time(uint index, uint ranges, ha_rows rows) double handler::keyread_time(uint index, uint ranges, ha_rows rows)
@@ -3261,7 +3261,8 @@ double handler::keyread_time(uint index, uint ranges, ha_rows rows)
if (table->file->is_clustering_key(index)) if (table->file->is_clustering_key(index))
len= table->s->stored_rec_length; len= table->s->stored_rec_length;
cost= ((double)rows*len/(stats.block_size+1)*INDEX_BLOCK_COPY_COST); cost= ((double)rows*len/(stats.block_size+1) *
INDEX_BLOCK_COPY_COST(table->in_use));
/* /*
We divide the cost with optimizer_cache_cost as ha_keyread_time() We divide the cost with optimizer_cache_cost as ha_keyread_time()
and ha_key_scan_time() will multiply the result value with and ha_key_scan_time() will multiply the result value with
@@ -3413,9 +3414,18 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode,
else else
dup_ref=ref+ALIGN_SIZE(ref_length); dup_ref=ref+ALIGN_SIZE(ref_length);
cached_table_flags= table_flags(); cached_table_flags= table_flags();
}
/* Copy current optimizer costs. Needed in case clone() is used */
set_optimizer_costs(table->in_use);
DBUG_ASSERT(optimizer_key_copy_cost >= 0.0);
DBUG_ASSERT(optimizer_key_next_find_cost >= 0.0);
DBUG_ASSERT(optimizer_row_copy_cost >= 0.0);
DBUG_ASSERT(optimizer_where_cost >= 0.0);
DBUG_ASSERT(optimizer_key_cmp_cost >= 0.0);
reset_statistics(); reset_statistics();
}
internal_tmp_table= MY_TEST(test_if_locked & HA_OPEN_INTERNAL_TABLE); internal_tmp_table= MY_TEST(test_if_locked & HA_OPEN_INTERNAL_TABLE);
DBUG_RETURN(error); DBUG_RETURN(error);
} }
@@ -8755,3 +8765,30 @@ Table_scope_and_contents_source_st::fix_period_fields(THD *thd,
} }
return false; return false;
} }
/*
Copy common optimizer cost variables to the engine
This is needed to provide fast acccess to these variables during
optimization (as we refer to them multiple times).
The other option would be to access them from thd, but that
would require a function call (as we cannot access THD from
an inline handler function) and two extra memory accesses
for each variable.
index_block_copy_cost is not copied as it is used so seldom.
*/
void handler::set_optimizer_costs(THD *thd)
{
optimizer_key_copy_cost= thd->variables.optimizer_key_copy_cost;
optimizer_key_next_find_cost=
thd->variables.optimizer_key_next_find_cost;
optimizer_row_copy_cost= thd->variables.optimizer_row_copy_cost;
optimizer_where_cost= thd->variables.optimizer_where_cost;
optimizer_key_cmp_cost= thd->variables.optimizer_key_cmp_cost;
set_optimizer_cache_cost(cache_hit_cost(thd->variables.
optimizer_cache_hit_ratio));
}

View File

@@ -3225,6 +3225,10 @@ public:
Updated from THD in open_tables() Updated from THD in open_tables()
*/ */
double optimizer_cache_cost; double optimizer_cache_cost;
double optimizer_key_next_find_cost;
double optimizer_row_copy_cost, optimizer_key_copy_cost;
double optimizer_where_cost, optimizer_key_cmp_cost;
ha_copy_info copy_info; ha_copy_info copy_info;
private: private:
@@ -3360,7 +3364,7 @@ public:
ref_length(sizeof(my_off_t)), ref_length(sizeof(my_off_t)),
ft_handler(0), inited(NONE), pre_inited(NONE), ft_handler(0), inited(NONE), pre_inited(NONE),
pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0), pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
optimizer_cache_cost((100-CACHE_HIT_RATIO)/100.0), optimizer_cache_cost((100-DEFAULT_CACHE_HIT_RATIO)/100.0),
tracker(NULL), tracker(NULL),
pushed_idx_cond(NULL), pushed_idx_cond(NULL),
pushed_idx_cond_keyno(MAX_KEY), pushed_idx_cond_keyno(MAX_KEY),
@@ -3624,7 +3628,7 @@ public:
inline double ha_scan_and_compare_time(ha_rows records) inline double ha_scan_and_compare_time(ha_rows records)
{ {
return (ha_scan_time() + return (ha_scan_time() +
(double) records * (ROW_COPY_COST + 1/TIME_FOR_COMPARE)); (double) records * (ROW_COPY_COST + WHERE_COST));
} }
virtual double avg_io_cost() virtual double avg_io_cost()
@@ -3632,6 +3636,13 @@ public:
return 1.0; return 1.0;
} }
virtual void set_optimizer_costs(THD *thd);
/*
Set cost for finding a row in the engine cache
This allows the handler to override the cost if there is no
caching of rows, like in heap or federatedx.
*/
virtual void set_optimizer_cache_cost(double cost) virtual void set_optimizer_cache_cost(double cost)
{ {
optimizer_cache_cost= cost; optimizer_cache_cost= cost;
@@ -3678,7 +3689,7 @@ public:
inline double ha_read_and_compare_time(uint index, uint ranges, ha_rows rows) inline double ha_read_and_compare_time(uint index, uint ranges, ha_rows rows)
{ {
return (ha_read_time(index, ranges, rows) + return (ha_read_time(index, ranges, rows) +
rows2double(rows) * (ROW_COPY_COST + 1/TIME_FOR_COMPARE)); rows2double(rows) * (ROW_COPY_COST + WHERE_COST));
} }
/* Cost of reading a row with rowid */ /* Cost of reading a row with rowid */
@@ -3754,7 +3765,7 @@ public:
inline double ha_key_scan_and_compare_time(uint index, ha_rows rows) inline double ha_key_scan_and_compare_time(uint index, ha_rows rows)
{ {
return (ha_key_scan_time(index) + return (ha_key_scan_time(index) +
(double) rows * (KEY_COPY_COST + 1/TIME_FOR_COMPARE)); (double) rows * (KEY_COPY_COST + WHERE_COST));
} }
virtual const key_map *keys_to_use_for_scanning() { return &key_map_empty; } virtual const key_map *keys_to_use_for_scanning() { return &key_map_empty; }

View File

@@ -343,7 +343,7 @@ handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
cost->cpu_cost= ha_read_time(keyno, 0, total_rows); cost->cpu_cost= ha_read_time(keyno, 0, total_rows);
cost->copy_cost= rows2double(total_rows) * ROW_COPY_COST; cost->copy_cost= rows2double(total_rows) * ROW_COPY_COST;
} }
cost->comp_cost= (rows2double(total_rows) / TIME_FOR_COMPARE + cost->comp_cost= (rows2double(total_rows) * WHERE_COST +
MULTI_RANGE_READ_SETUP_COST); MULTI_RANGE_READ_SETUP_COST);
} }
DBUG_PRINT("statistics", DBUG_PRINT("statistics",
@@ -427,7 +427,8 @@ ha_rows handler::multi_range_read_info(uint keyno, uint n_ranges, uint n_rows,
else else
{ {
/* /*
Same as above, but take into account copying the key to the upper level. Same as above, but take into account copying the key to the upper
level.
*/ */
cost->idx_cpu_cost= (keyread_time(keyno, 1, n_rows) + cost->idx_cpu_cost= (keyread_time(keyno, 1, n_rows) +
(n_ranges-1) * range_lookup_cost); (n_ranges-1) * range_lookup_cost);
@@ -440,7 +441,7 @@ ha_rows handler::multi_range_read_info(uint keyno, uint n_ranges, uint n_rows,
cost->cpu_cost= read_time(keyno, n_ranges, n_rows); cost->cpu_cost= read_time(keyno, n_ranges, n_rows);
cost->copy_cost= rows2double(n_rows) * ROW_COPY_COST; cost->copy_cost= rows2double(n_rows) * ROW_COPY_COST;
} }
cost->comp_cost= rows2double(n_rows) / TIME_FOR_COMPARE; cost->comp_cost= rows2double(n_rows) * WHERE_COST;
return 0; return 0;
} }
@@ -2067,7 +2068,7 @@ void get_sort_and_sweep_cost(TABLE *table, ha_rows nrows, Cost_estimate *cost)
{ {
get_sweep_read_cost(table, nrows, FALSE, cost); get_sweep_read_cost(table, nrows, FALSE, cost);
/* Add cost of qsort call: n * log2(n) * cost(rowid_comparison) */ /* Add cost of qsort call: n * log2(n) * cost(rowid_comparison) */
double cmp_op= rows2double(nrows) * (1.0 / TIME_FOR_COMPARE_ROWID); double cmp_op= rows2double(nrows) * ROWID_COMPARE_COST_THD(table->in_use);
if (cmp_op < 3) if (cmp_op < 3)
cmp_op= 3; cmp_op= 3;
cost->cpu_cost += cmp_op * log2(cmp_op); cost->cpu_cost += cmp_op * log2(cmp_op);

View File

@@ -5069,7 +5069,7 @@ static void dbug_print_singlepoint_range(SEL_ARG **start, uint num)
get_sweep_read_cost() get_sweep_read_cost()
param Parameter from test_quick_select param Parameter from test_quick_select
records # of records to be retrieved records # of records to be retrieved
add_time_for_compare If set, add cost of WHERE clause (TIME_FOR_COMPARE) add_time_for_compare If set, add cost of WHERE clause (WHERE_COST)
RETURN RETURN
cost of sweep cost of sweep
*/ */
@@ -5080,7 +5080,8 @@ static double get_sweep_read_cost(const PARAM *param, ha_rows records,
DBUG_ENTER("get_sweep_read_cost"); DBUG_ENTER("get_sweep_read_cost");
#ifndef OLD_SWEEP_COST #ifndef OLD_SWEEP_COST
double cost= (param->table->file->ha_rnd_pos_time(records) + double cost= (param->table->file->ha_rnd_pos_time(records) +
(add_time_for_compare ? records / TIME_FOR_COMPARE : 0)); (add_time_for_compare ?
records * param->thd->variables.optimizer_where_cost : 0));
DBUG_PRINT("return", ("cost: %g", cost)); DBUG_PRINT("return", ("cost: %g", cost));
DBUG_RETURN(cost); DBUG_RETURN(cost);
#else #else
@@ -5132,7 +5133,7 @@ static double get_sweep_read_cost(const PARAM *param, ha_rows records,
*/ */
result= busy_blocks; result= busy_blocks;
} }
result+= rows2double(n_rows) * ROW_COPY_COST; result+= rows2double(n_rows) * ROW_COPY_COST_THD(param->table->thd);
} }
DBUG_PRINT("return",("cost: %g", result)); DBUG_PRINT("return",("cost: %g", result));
DBUG_RETURN(result); DBUG_RETURN(result);
@@ -5345,8 +5346,8 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
Add one ROWID comparison for each row retrieved on non-CPK scan. (it Add one ROWID comparison for each row retrieved on non-CPK scan. (it
is done in QUICK_RANGE_SELECT::row_in_ranges) is done in QUICK_RANGE_SELECT::row_in_ranges)
*/ */
double rid_comp_cost= (rows2double(non_cpk_scan_records) / double rid_comp_cost= (rows2double(non_cpk_scan_records) *
TIME_FOR_COMPARE_ROWID); ROWID_COMPARE_COST_THD(param->thd));
imerge_cost+= rid_comp_cost; imerge_cost+= rid_comp_cost;
trace_best_disjunct.add("cost_of_mapping_rowid_in_non_clustered_pk_scan", trace_best_disjunct.add("cost_of_mapping_rowid_in_non_clustered_pk_scan",
rid_comp_cost); rid_comp_cost);
@@ -5354,7 +5355,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
/* Calculate cost(rowid_to_row_scan) */ /* Calculate cost(rowid_to_row_scan) */
{ {
/* imerge_cost already includes TIME_FOR_COMPARE */ /* imerge_cost already includes WHERE_COST */
double sweep_cost= get_sweep_read_cost(param, non_cpk_scan_records, 0); double sweep_cost= get_sweep_read_cost(param, non_cpk_scan_records, 0);
imerge_cost+= sweep_cost; imerge_cost+= sweep_cost;
trace_best_disjunct. trace_best_disjunct.
@@ -5392,7 +5393,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
param->imerge_cost_buff, (uint)non_cpk_scan_records, param->imerge_cost_buff, (uint)non_cpk_scan_records,
param->table->file->ref_length, param->table->file->ref_length,
(size_t)param->thd->variables.sortbuff_size, (size_t)param->thd->variables.sortbuff_size,
TIME_FOR_COMPARE_ROWID, ROWID_COMPARE_COST_THD(param->thd),
FALSE, NULL); FALSE, NULL);
imerge_cost+= dup_removal_cost; imerge_cost+= dup_removal_cost;
if (unlikely(trace_best_disjunct.trace_started())) if (unlikely(trace_best_disjunct.trace_started()))
@@ -5506,8 +5507,8 @@ skip_to_ror_scan:
double roru_total_cost; double roru_total_cost;
roru_total_cost= (roru_index_costs + roru_total_cost= (roru_index_costs +
rows2double(roru_total_records)*log((double)n_child_scans) / rows2double(roru_total_records)*log((double)n_child_scans) *
(TIME_FOR_COMPARE_ROWID * M_LN2) + ROWID_COMPARE_COST_THD(param->thd) / M_LN2 +
get_sweep_read_cost(param, roru_total_records, 0)); get_sweep_read_cost(param, roru_total_records, 0));
DBUG_PRINT("info", ("ROR-union: cost %g, %zu members", DBUG_PRINT("info", ("ROR-union: cost %g, %zu members",
@@ -5663,7 +5664,7 @@ typedef struct st_common_index_intersect_info
{ {
PARAM *param; /* context info for range optimizations */ PARAM *param; /* context info for range optimizations */
uint key_size; /* size of a ROWID element stored in Unique object */ uint key_size; /* size of a ROWID element stored in Unique object */
double compare_factor; /* 1/compare - cost to compare two ROWIDs */ double compare_factor; /* cost to compare two ROWIDs */
size_t max_memory_size; /* maximum space allowed for Unique objects */ size_t max_memory_size; /* maximum space allowed for Unique objects */
ha_rows table_cardinality; /* estimate of the number of records in table */ ha_rows table_cardinality; /* estimate of the number of records in table */
double cutoff_cost; /* discard index intersects with greater costs */ double cutoff_cost; /* discard index intersects with greater costs */
@@ -5877,7 +5878,7 @@ bool prepare_search_best_index_intersect(PARAM *param,
common->param= param; common->param= param;
common->key_size= table->file->ref_length; common->key_size= table->file->ref_length;
common->compare_factor= TIME_FOR_COMPARE_ROWID; common->compare_factor= ROWID_COMPARE_COST_THD(param->thd);
common->max_memory_size= (size_t)param->thd->variables.sortbuff_size; common->max_memory_size= (size_t)param->thd->variables.sortbuff_size;
common->cutoff_cost= cutoff_cost; common->cutoff_cost= cutoff_cost;
common->cpk_scan= NULL; common->cpk_scan= NULL;
@@ -5934,7 +5935,7 @@ bool prepare_search_best_index_intersect(PARAM *param,
continue; continue;
} }
cost= table->opt_range[(*index_scan)->keynr].index_only_fetch_cost(); cost= table->opt_range[(*index_scan)->keynr].index_only_fetch_cost(thd);
idx_scan.add("cost", cost); idx_scan.add("cost", cost);
@@ -6298,8 +6299,8 @@ double get_cpk_filter_cost(ha_rows filtered_records,
INDEX_SCAN_INFO *cpk_scan, INDEX_SCAN_INFO *cpk_scan,
double compare_factor) double compare_factor)
{ {
return log((double) (cpk_scan->range_count+1)) / (compare_factor * M_LN2) * return (log((double) (cpk_scan->range_count+1)) * compare_factor / M_LN2 *
filtered_records; filtered_records);
} }
@@ -7054,11 +7055,11 @@ static bool ror_intersect_add(ROR_INTERSECT_INFO *info,
{ {
/* /*
CPK scan is used to filter out rows. We apply filtering for CPK scan is used to filter out rows. We apply filtering for
each record of every scan. Assuming 1/TIME_FOR_COMPARE_ROWID each record of every scan. Assuming ROWID_COMPARE_COST
per check this gives us: per check this gives us:
*/ */
const double idx_cost= (rows2double(info->index_records) / const double idx_cost= (rows2double(info->index_records) *
TIME_FOR_COMPARE_ROWID); ROWID_COMPARE_COST_THD(info->param->thd));
info->index_scan_costs+= idx_cost; info->index_scan_costs+= idx_cost;
trace_costs->add("index_scan_cost", idx_cost); trace_costs->add("index_scan_cost", idx_cost);
} }
@@ -7367,7 +7368,7 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
Adjust row count and add the cost of comparing the final rows to the Adjust row count and add the cost of comparing the final rows to the
WHERE clause WHERE clause
*/ */
cmp_cost= intersect_best->out_rows/TIME_FOR_COMPARE; cmp_cost= intersect_best->out_rows * thd->variables.optimizer_where_cost;
/* Ok, return ROR-intersect plan if we have found one */ /* Ok, return ROR-intersect plan if we have found one */
TRP_ROR_INTERSECT *trp= NULL; TRP_ROR_INTERSECT *trp= NULL;
@@ -7536,9 +7537,9 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
tree->ror_scans, ror_scan_mark);); tree->ror_scans, ror_scan_mark););
/* Add priority queue use cost. */ /* Add priority queue use cost. */
total_cost += rows2double(records)* total_cost += (rows2double(records) *
log((double)(ror_scan_mark - tree->ror_scans)) / log((double)(ror_scan_mark - tree->ror_scans)) *
(TIME_FOR_COMPARE_ROWID * M_LN2); ROWID_COMPARE_COST_THD(param->thd) / M_LN2);
DBUG_PRINT("info", ("Covering ROR-intersect full cost: %g", total_cost)); DBUG_PRINT("info", ("Covering ROR-intersect full cost: %g", total_cost));
/* TODO: Add TIME_FOR_COMPARE cost to total_cost */ /* TODO: Add TIME_FOR_COMPARE cost to total_cost */
@@ -15093,7 +15094,7 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
group. To make the CPU cost reflect this, we estimate the CPU cost group. To make the CPU cost reflect this, we estimate the CPU cost
as the sum of: as the sum of:
1. Cost for evaluating the condition for each num_group 1. Cost for evaluating the condition for each num_group
(1/TIME_FOR_COMPARE_IDX) (similarly as for index scan). KEY_COMPARE_COST (similarly as for index scan).
2. Cost for navigating the index structure (assuming a b-tree). 2. Cost for navigating the index structure (assuming a b-tree).
Note: We only add the cost for one index comparision per block. For a Note: We only add the cost for one index comparision per block. For a
b-tree the number of comparisons will be larger. However the cost b-tree the number of comparisons will be larger. However the cost
@@ -15102,13 +15103,15 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
TODO: This cost should be provided by the storage engine. TODO: This cost should be provided by the storage engine.
3. Cost for comparing the row with the where clause 3. Cost for comparing the row with the where clause
*/ */
const THD *thd= table->in_use;
const double tree_traversal_cost= const double tree_traversal_cost=
ceil(log(static_cast<double>(table_records))/ ceil(log(static_cast<double>(table_records))/
log(static_cast<double>(keys_per_block))) * log(static_cast<double>(keys_per_block))) *
1/(TIME_FOR_COMPARE_IDX); thd->variables.optimizer_key_cmp_cost;
const double cpu_cost= (num_groups * const double cpu_cost= (num_groups *
(tree_traversal_cost + 1/TIME_FOR_COMPARE)); (tree_traversal_cost +
thd->variables.optimizer_where_cost));
*read_cost= io_cost + cpu_cost; *read_cost= io_cost + cpu_cost;
*records= num_groups; *records= num_groups;

View File

@@ -1449,6 +1449,7 @@ void get_delayed_table_estimates(TABLE *table,
Item_in_subselect *item= table->pos_in_table_list->jtbm_subselect; Item_in_subselect *item= table->pos_in_table_list->jtbm_subselect;
Table_function_json_table *table_function= Table_function_json_table *table_function=
table->pos_in_table_list->table_function; table->pos_in_table_list->table_function;
handler *file= table->file;
if (table_function) if (table_function)
{ {
@@ -1468,9 +1469,10 @@ void get_delayed_table_estimates(TABLE *table,
/* Calculate cost of scanning the temptable */ /* Calculate cost of scanning the temptable */
double data_size= COST_MULT(item->jtbm_record_count, double data_size= COST_MULT(item->jtbm_record_count,
hash_sj_engine->tmp_table->s->reclength); hash_sj_engine->tmp_table->s->reclength);
/* Do like in handler::ha_scan_and_compare_time, but ignore the where cost */ /* Do like in handler::ha_scan_and_compare_time, but ignore the where cost */
*scan_time= ((data_size/table->file->stats.block_size+2) * *scan_time= ((data_size/table->file->stats.block_size+2) *
table->file->avg_io_cost()) + *out_rows * ROW_COPY_COST; table->file->avg_io_cost()) + *out_rows * file->ROW_COPY_COST;
} }

View File

@@ -20,14 +20,6 @@
/* This file includes costs variables used by the optimizer */ /* This file includes costs variables used by the optimizer */
/**
The following is used to decide if MariaDB should use table scanning
instead of reading with keys. The number says how many evaluation of the
WHERE clause is comparable to reading one extra row from a table.
*/
#define TIME_FOR_COMPARE 5.0 // 5 WHERE compares == one read
#define TIME_FOR_COMPARE_IDX 20.0
/* /*
The table/index cache hit ratio in %. 0 means that a searched for key or row The table/index cache hit ratio in %. 0 means that a searched for key or row
will never be in the cache while 100 means it always in the cache. will never be in the cache while 100 means it always in the cache.
@@ -43,7 +35,7 @@
Note that avg_io_cost() is multipled with this constant! Note that avg_io_cost() is multipled with this constant!
*/ */
#define CACHE_HIT_RATIO 50 #define DEFAULT_CACHE_HIT_RATIO 50
/* Convert ratio to cost */ /* Convert ratio to cost */
@@ -53,14 +45,23 @@ static inline double cache_hit_cost(uint ratio)
} }
/* /*
Cost of finding and copying keys of a total length of 'blocksize' Base cost for finding keys and rows from the engine is 1.0
used in handler::keyread_time() All other costs should be proportional to these
*/ */
#define INDEX_BLOCK_COPY_COST ((double) 1 / 5.0)
/* Cost for finding the first key in a key scan */ /* Cost for finding the first key in a key scan */
#define KEY_LOOKUP_COST ((double) 1.0) #define KEY_LOOKUP_COST ((double) 1.0)
/* Cost of finding a key from a row_ID (not used for clustered keys) */ /* Cost of finding a key from a row_ID (not used for clustered keys) */
#define ROW_LOOKUP_COST ((double) 1.0) #define ROW_LOOKUP_COST ((double) 1.0)
/*
Cost of finding and copying keys from the storage engine index cache to
an internal cache as part of an index scan.
Used in handler::keyread_time()
*/
#define DEFAULT_INDEX_BLOCK_COPY_COST ((double) 1 / 5.0)
#define INDEX_BLOCK_COPY_COST(THD) ((THD)->variables.optimizer_index_block_copy_cost)
/* /*
Cost of finding the next row during table scan and copying it to Cost of finding the next row during table scan and copying it to
'table->record'. 'table->record'.
@@ -70,7 +71,10 @@ static inline double cache_hit_cost(uint ratio)
too big then MariaDB will used key lookup even when table scan is too big then MariaDB will used key lookup even when table scan is
better. better.
*/ */
#define ROW_COPY_COST ((double) 1.0 / 20.0) #define DEFAULT_ROW_COPY_COST ((double) 1.0 / 20.0)
#define ROW_COPY_COST optimizer_row_copy_cost
#define ROW_COPY_COST_THD(THD) ((THD)->variables.optimizer_row_copy_cost)
/* /*
Cost of finding the next key during index scan and copying it to Cost of finding the next key during index scan and copying it to
'table->record' 'table->record'
@@ -79,13 +83,40 @@ static inline double cache_hit_cost(uint ratio)
as with table scans there are no key read (KEY_LOOKUP_COST) and as with table scans there are no key read (KEY_LOOKUP_COST) and
fewer disk reads. fewer disk reads.
*/ */
#define KEY_COPY_COST ((double) 1.0 / 40.0) #define DEFAULT_KEY_COPY_COST ((double) 1.0 / 40.0)
#define KEY_COPY_COST optimizer_key_copy_cost
#define KEY_COPY_COST_THD(THD) ((THD)->variables.optimizer_key_copy_cost)
/* /*
Cost of finding the next index entry and checking it against filter Cost of finding the next index entry and checking it against filter
This cost is very low as it's done inside the storage engine. This cost is very low as it's done inside the storage engine.
Should be smaller than KEY_COPY_COST. Should be smaller than KEY_COPY_COST.
*/ */
#define KEY_NEXT_FIND_COST ((double) 1.0 / 80.0) #define DEFAULT_KEY_NEXT_FIND_COST ((double) 1.0 / 80.0)
#define KEY_NEXT_FIND_COST optimizer_next_find_cost
/**
The following is used to decide if MariaDB should use table scanning
instead of reading with keys. The number says how many evaluation of the
WHERE clause is comparable to reading one extra row from a table.
*/
#define DEFAULT_WHERE_COST (1 / 5.0)
#define WHERE_COST optimizer_where_cost
#define WHERE_COST_THD(THD) ((THD)->variables.optimizer_where_cost)
#define DEFAULT_KEY_COMPARE_COST (1 / 20.0)
#define KEY_COMPARE_COST optimizer_key_cmp_cost
/*
Cost of comparing two rowids. This is set relative to KEY_COMPARE_COST
This is usally just a memcmp!
*/
#define ROWID_COMPARE_COST KEY_COMPARE_COST/10.0
#define ROWID_COMPARE_COST_THD(THD) ((THD)->variables.KEY_COMPARE_COST / 10.0)
/*
Setup cost for different operations
*/
/* Extra cost for doing a range scan. Used to prefer 'ref' over range */ /* Extra cost for doing a range scan. Used to prefer 'ref' over range */
#define MULTI_RANGE_READ_SETUP_COST (double) (1.0 / 50.0) #define MULTI_RANGE_READ_SETUP_COST (double) (1.0 / 50.0)
@@ -105,12 +136,6 @@ static inline double cache_hit_cost(uint ratio)
*/ */
#define MIN_ROWS_AFTER_FILTERING 1.0 #define MIN_ROWS_AFTER_FILTERING 1.0
/**
Number of comparisons of table rowids equivalent to reading one row from a
table.
*/
#define TIME_FOR_COMPARE_ROWID (TIME_FOR_COMPARE*100)
/* /*
cost1 is better that cost2 only if cost1 + COST_EPS < cost2 cost1 is better that cost2 only if cost1 + COST_EPS < cost2
The main purpose of this is to ensure we use the first index or plan The main purpose of this is to ensure we use the first index or plan

View File

@@ -23,7 +23,7 @@
#include "opt_trace.h" #include "opt_trace.h"
/* /*
KEY_NEXT_FIND_COST below is the cost of finding the next possible key key_next_find_cost below is the cost of finding the next possible key
and calling handler_rowid_filter_check() to check it against the filter and calling handler_rowid_filter_check() to check it against the filter
*/ */
@@ -32,7 +32,7 @@ lookup_cost(Rowid_filter_container_type cont_type)
{ {
switch (cont_type) { switch (cont_type) {
case SORTED_ARRAY_CONTAINER: case SORTED_ARRAY_CONTAINER:
return log(est_elements)*0.01+KEY_NEXT_FIND_COST; return log(est_elements)*0.01+key_next_find_cost;
default: default:
DBUG_ASSERT(0); DBUG_ASSERT(0);
return 0; return 0;
@@ -51,7 +51,7 @@ double Range_rowid_filter_cost_info::
avg_access_and_eval_gain_per_row(Rowid_filter_container_type cont_type, avg_access_and_eval_gain_per_row(Rowid_filter_container_type cont_type,
double cost_of_row_fetch) double cost_of_row_fetch)
{ {
return (cost_of_row_fetch+1.0/TIME_FOR_COMPARE) * (1 - selectivity) - return (cost_of_row_fetch + where_cost) * (1 - selectivity) -
lookup_cost(cont_type); lookup_cost(cont_type);
} }
@@ -125,6 +125,8 @@ void Range_rowid_filter_cost_info::init(Rowid_filter_container_type cont_type,
key_no= idx; key_no= idx;
est_elements= (ulonglong) table->opt_range[key_no].rows; est_elements= (ulonglong) table->opt_range[key_no].rows;
cost_of_building_range_filter= build_cost(container_type); cost_of_building_range_filter= build_cost(container_type);
where_cost= tab->in_use->variables.optimizer_where_cost;
key_next_find_cost= tab->in_use->variables.optimizer_key_next_find_cost;
selectivity= est_elements/((double) table->stat_records()); selectivity= est_elements/((double) table->stat_records());
gain= avg_access_and_eval_gain_per_row(container_type, gain= avg_access_and_eval_gain_per_row(container_type,
tab->file->optimizer_cache_cost); tab->file->optimizer_cache_cost);
@@ -147,7 +149,7 @@ Range_rowid_filter_cost_info::build_cost(Rowid_filter_container_type cont_type)
double cost; double cost;
DBUG_ASSERT(table->opt_range_keys.is_set(key_no)); DBUG_ASSERT(table->opt_range_keys.is_set(key_no));
cost= table->opt_range[key_no].index_only_fetch_cost(); cost= table->opt_range[key_no].index_only_fetch_cost(table->in_use);
switch (cont_type) { switch (cont_type) {
@@ -523,7 +525,8 @@ TABLE::best_range_rowid_filter_for_partial_join(uint access_key_no,
cost_of_rejected_rows= index_only_cost * (1 - filter->selectivity); cost_of_rejected_rows= index_only_cost * (1 - filter->selectivity);
new_cost= (cost_of_accepted_rows + cost_of_rejected_rows + new_cost= (cost_of_accepted_rows + cost_of_rejected_rows +
records * filter->lookup_cost()); records * filter->lookup_cost());
new_total_cost= ((new_cost + new_records/TIME_FOR_COMPARE) * prev_records + new_total_cost= ((new_cost + new_records *
in_use->variables.optimizer_where_cost) * prev_records +
filter->get_setup_cost()); filter->get_setup_cost());
if (best_filter_gain > new_total_cost) if (best_filter_gain > new_total_cost)

View File

@@ -406,6 +406,7 @@ class Range_rowid_filter_cost_info final: public Sql_alloc
/* The index whose range scan would be used to build the range filter */ /* The index whose range scan would be used to build the range filter */
uint key_no; uint key_no;
double cost_of_building_range_filter; double cost_of_building_range_filter;
double where_cost, key_next_find_cost;
/* /*
(gain*row_combinations)-cost_of_building_range_filter yields the gain of (gain*row_combinations)-cost_of_building_range_filter yields the gain of
the filter for 'row_combinations' key tuples of the index key_no the filter for 'row_combinations' key tuples of the index key_no
@@ -466,7 +467,7 @@ public:
*/ */
inline double get_cmp_gain(double row_combinations) inline double get_cmp_gain(double row_combinations)
{ {
return row_combinations * (1 - selectivity) / TIME_FOR_COMPARE; return (row_combinations * (1 - selectivity) * where_cost);
} }
Rowid_filter_container *create_container(); Rowid_filter_container *create_container();

View File

@@ -692,6 +692,10 @@ typedef struct system_variables
ulonglong slave_skip_counter; ulonglong slave_skip_counter;
ulonglong max_relay_log_size; ulonglong max_relay_log_size;
double optimizer_index_block_copy_cost, optimizer_key_next_find_cost;
double optimizer_row_copy_cost, optimizer_key_copy_cost;
double optimizer_where_cost, optimizer_key_cmp_cost;
ha_rows select_limit; ha_rows select_limit;
ha_rows max_join_size; ha_rows max_join_size;
ha_rows expensive_subquery_limit; ha_rows expensive_subquery_limit;
@@ -776,7 +780,7 @@ typedef struct system_variables
uint group_concat_max_len; uint group_concat_max_len;
uint eq_range_index_dive_limit; uint eq_range_index_dive_limit;
uint optimizer_cache_hit_ratio; uint optimizer_cache_hit_ratio; // Stored in handler::optimizer_cache_cost
int max_user_connections; int max_user_connections;
/** /**

View File

@@ -7825,7 +7825,7 @@ static double matching_candidates_in_table(JOIN_TAB *s,
KEY_COPY_COST as for filtering there is no copying of not accepted KEY_COPY_COST as for filtering there is no copying of not accepted
keys. keys.
TIME_FOR_COMPARE cost is not added to any result. WHERE_COST cost is not added to any result.
*/ */
INDEX_READ_COST cost_for_index_read(const THD *thd, const TABLE *table, INDEX_READ_COST cost_for_index_read(const THD *thd, const TABLE *table,
@@ -7850,14 +7850,14 @@ INDEX_READ_COST cost_for_index_read(const THD *thd, const TABLE *table,
records to compute the record copy cost. records to compute the record copy cost.
*/ */
cost.read_cost= (cost.index_only_cost + cost.read_cost= (cost.index_only_cost +
rows2double(records) * ROW_COPY_COST); rows2double(records) * ROW_COPY_COST_THD(thd));
} }
else if (table->covering_keys.is_set(key) && !table->no_keyread) else if (table->covering_keys.is_set(key) && !table->no_keyread)
{ {
cost.index_only_cost= file->ha_keyread_time(key, 1, rows_adjusted); cost.index_only_cost= file->ha_keyread_time(key, 1, rows_adjusted);
/* Same computation as in ha_keyread_and_copy_time() */ /* Same computation as in ha_keyread_and_copy_time() */
cost.read_cost= (cost.index_only_cost + cost.read_cost= (cost.index_only_cost +
rows2double(records) * KEY_COPY_COST); rows2double(records) * KEY_COPY_COST_THD(thd));
} }
else else
{ {
@@ -7868,7 +7868,7 @@ INDEX_READ_COST cost_for_index_read(const THD *thd, const TABLE *table,
*/ */
cost.read_cost= (cost.index_only_cost + cost.read_cost= (cost.index_only_cost +
file->ha_read_time(key, 0, rows_adjusted) + file->ha_read_time(key, 0, rows_adjusted) +
rows2double(records) * ROW_COPY_COST); rows2double(records) * ROW_COPY_COST_THD(thd));
} }
DBUG_PRINT("statistics", ("index_cost: %.3f full_cost: %.3f", DBUG_PRINT("statistics", ("index_cost: %.3f full_cost: %.3f",
cost.index_only_cost, cost.read_cost)); cost.index_only_cost, cost.read_cost));
@@ -7882,7 +7882,7 @@ INDEX_READ_COST cost_for_index_read(const THD *thd, const TABLE *table,
@param thd Thread handler @param thd Thread handler
@param table Table @param table Table
@param cost Pointer to cost for *records_arg rows, not including @param cost Pointer to cost for *records_arg rows, not including
TIME_FOR_COMPARE cost. WHERE_COST cost.
Will be updated to new cost if filter is used. Will be updated to new cost if filter is used.
@param records_arg Pointer to number of records for the current key. @param records_arg Pointer to number of records for the current key.
Will be updated to records after filter, if filter is Will be updated to records after filter, if filter is
@@ -7940,11 +7940,11 @@ apply_filter(THD *thd, TABLE *table, double *cost, double *records_arg,
ranges * KEY_LOOKUP_COST * io_cost * ranges * KEY_LOOKUP_COST * io_cost *
table->file->optimizer_cache_cost) + table->file->optimizer_cache_cost) +
cost_of_rejected_rows + filter_lookup_cost); cost_of_rejected_rows + filter_lookup_cost);
new_total_cost= ((new_cost + new_records/TIME_FOR_COMPARE) * prev_records + new_total_cost= ((new_cost + new_records * WHERE_COST_THD(thd)) *
filter_startup_cost); prev_records + filter_startup_cost);
DBUG_ASSERT(new_cost >= 0 && new_records >= 0); DBUG_ASSERT(new_cost >= 0 && new_records >= 0);
use_filter= ((*cost + records/TIME_FOR_COMPARE) * prev_records > use_filter= ((*cost + records * WHERE_COST_THD(thd)) * prev_records >
new_total_cost); new_total_cost);
if (unlikely(thd->trace_started())) if (unlikely(thd->trace_started()))
@@ -8638,7 +8638,7 @@ best_access_path(JOIN *join,
tmp, index_only_cost, tmp, index_only_cost,
1, record_count); 1, record_count);
} }
tmp= COST_ADD(tmp, records_after_filter/TIME_FOR_COMPARE); tmp= COST_ADD(tmp, records_after_filter * WHERE_COST_THD(thd));
tmp= COST_MULT(tmp, record_count); tmp= COST_MULT(tmp, record_count);
tmp= COST_ADD(tmp, startup_cost); tmp= COST_ADD(tmp, startup_cost);
if (unlikely(trace_access_idx.trace_started())) if (unlikely(trace_access_idx.trace_started()))
@@ -8753,7 +8753,8 @@ best_access_path(JOIN *join,
We assume here that, thanks to the hash, we don't have to compare all We assume here that, thanks to the hash, we don't have to compare all
row combinations, only a HASH_FANOUT (10%) rows in the cache. row combinations, only a HASH_FANOUT (10%) rows in the cache.
*/ */
cmp_time= (rnd_records * record_count * HASH_FANOUT / TIME_FOR_COMPARE); cmp_time= (rnd_records * record_count * HASH_FANOUT *
WHERE_COST_THD(thd));
tmp= COST_ADD(tmp, cmp_time); tmp= COST_ADD(tmp, cmp_time);
best_cost= tmp; best_cost= tmp;
@@ -8840,7 +8841,7 @@ best_access_path(JOIN *join,
- skip rows which does not satisfy WHERE constraints - skip rows which does not satisfy WHERE constraints
Note that s->quick->read_time includes the cost of comparing Note that s->quick->read_time includes the cost of comparing
the row with the where clause (TIME_FOR_COMPARE) the row with the where clause (WHERE_COST)
TODO: TODO:
We take into account possible use of join cache for ALL/index We take into account possible use of join cache for ALL/index
@@ -8889,8 +8890,9 @@ best_access_path(JOIN *join,
if (filter) if (filter)
{ {
tmp= filter_cost; tmp= filter_cost;
/* Filter returns cost without TIME_FOR_COMPARE */ /* Filter returns cost without WHERE_COST */
tmp= COST_ADD(tmp, records_after_filter / TIME_FOR_COMPARE); tmp= COST_ADD(tmp, records_after_filter *
WHERE_COST_THD(thd));
tmp= COST_MULT(tmp, record_count); tmp= COST_MULT(tmp, record_count);
tmp= COST_ADD(tmp, startup_cost); tmp= COST_ADD(tmp, startup_cost);
startup_cost= 0; // Avoid adding it later startup_cost= 0; // Avoid adding it later
@@ -8927,7 +8929,7 @@ best_access_path(JOIN *join,
type= JT_NEXT; type= JT_NEXT;
tmp= cost.read_cost; tmp= cost.read_cost;
/* Calculate cost of checking the attached WHERE */ /* Calculate cost of checking the attached WHERE */
tmp= COST_ADD(cost.read_cost, s->records / TIME_FOR_COMPARE); tmp= COST_ADD(cost.read_cost, s->records * WHERE_COST_THD(thd));
} }
else // table scan else // table scan
{ {
@@ -8979,8 +8981,8 @@ best_access_path(JOIN *join,
row. row.
*/ */
cmp_time= (rnd_records * record_count * cmp_time= (rnd_records * record_count *
(ROW_COPY_COST * (idx - join->const_tables) + (ROW_COPY_COST_THD(thd) * (idx - join->const_tables) +
1 / TIME_FOR_COMPARE)); WHERE_COST_THD(thd)));
tmp= COST_ADD(tmp, cmp_time); tmp= COST_ADD(tmp, cmp_time);
} }
} }
@@ -14848,7 +14850,8 @@ void JOIN_TAB::cleanup()
void JOIN_TAB::estimate_scan_time() void JOIN_TAB::estimate_scan_time()
{ {
double copy_cost= ROW_COPY_COST; THD *thd= join->thd;
double copy_cost= ROW_COPY_COST_THD(thd);
cached_covering_key= MAX_KEY; cached_covering_key= MAX_KEY;
if (table->is_created()) if (table->is_created())
@@ -14873,7 +14876,7 @@ void JOIN_TAB::estimate_scan_time()
{ {
cached_covering_key= find_shortest_key(table, &table->covering_keys); cached_covering_key= find_shortest_key(table, &table->covering_keys);
read_time= table->file->ha_key_scan_time(cached_covering_key); read_time= table->file->ha_key_scan_time(cached_covering_key);
copy_cost= KEY_COPY_COST; copy_cost= KEY_COPY_COST_THD(thd);
} }
else else
read_time= table->file->ha_scan_time(); read_time= table->file->ha_scan_time();
@@ -14888,7 +14891,7 @@ void JOIN_TAB::estimate_scan_time()
found_records= records; found_records= records;
cached_scan_time= read_time; cached_scan_time= read_time;
cached_scan_and_compare_time= (read_time + records * cached_scan_and_compare_time= (read_time + records *
(copy_cost + 1/TIME_FOR_COMPARE)); (copy_cost + WHERE_COST_THD(thd)));
} }
@@ -29967,7 +29970,7 @@ static bool get_range_limit_read_cost(const JOIN_TAB *tab,
best_cost *= rows_limit_for_quick / best_rows; best_cost *= rows_limit_for_quick / best_rows;
best_rows = rows_limit_for_quick; best_rows = rows_limit_for_quick;
} }
*read_time= best_cost + best_rows/TIME_FOR_COMPARE; *read_time= best_cost + best_rows * WHERE_COST_THD(table->in_use);
*read_rows= best_rows; *read_rows= best_rows;
res= true; res= true;
} }
@@ -30280,7 +30283,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
(ha_rows) tab->worst_seeks : (ha_rows) tab->worst_seeks :
HA_ROWS_MAX); HA_ROWS_MAX);
index_scan_time= (cost.read_cost + index_scan_time= (cost.read_cost +
select_limit / TIME_FOR_COMPARE); select_limit * WHERE_COST_THD(thd));
} }
#else #else
/* /*

View File

@@ -6968,7 +6968,8 @@ static Sys_var_uint Sys_in_subquery_conversion_threshold(
"IN predicate that triggers its conversion to IN subquery. Set to " "IN predicate that triggers its conversion to IN subquery. Set to "
"0 to disable the conversion.", "0 to disable the conversion.",
SESSION_VAR(in_subquery_conversion_threshold), CMD_LINE(REQUIRED_ARG), SESSION_VAR(in_subquery_conversion_threshold), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, UINT_MAX), DEFAULT(IN_SUBQUERY_CONVERSION_THRESHOLD), BLOCK_SIZE(1)); VALID_RANGE(0, UINT_MAX), DEFAULT(IN_SUBQUERY_CONVERSION_THRESHOLD),
BLOCK_SIZE(1));
static Sys_var_ulong Sys_optimizer_max_sel_arg_weight( static Sys_var_ulong Sys_optimizer_max_sel_arg_weight(
"optimizer_max_sel_arg_weight", "optimizer_max_sel_arg_weight",
@@ -6985,7 +6986,50 @@ static Sys_var_uint Sys_optimizer_cache_hit_ratio(
"optimizer_cache_hit_ratio", "optimizer_cache_hit_ratio",
"Expected hit rate of the row and index cache in storage engines. " "Expected hit rate of the row and index cache in storage engines. "
"The value should be an integer between 0 and 99, where 0 means cache is " "The value should be an integer between 0 and 99, where 0 means cache is "
"empty and 99 means that value is almost always in the cache", "empty and 99 means that value is almost always in the cache.",
SESSION_VAR(optimizer_cache_hit_ratio), CMD_LINE(REQUIRED_ARG), SESSION_VAR(optimizer_cache_hit_ratio), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, 99), DEFAULT(CACHE_HIT_RATIO), 1, NO_MUTEX_GUARD, VALID_RANGE(0, 99), DEFAULT(DEFAULT_CACHE_HIT_RATIO), 1, NO_MUTEX_GUARD,
NOT_IN_BINLOG);
static Sys_var_double Sys_optimizer_key_copy_cost(
"optimizer_key_copy_cost",
"Cost of finding the next key in the engine and copying it to the SQL layer.",
SESSION_VAR(optimizer_key_copy_cost), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, 1), DEFAULT(DEFAULT_KEY_COPY_COST), NO_MUTEX_GUARD,
NOT_IN_BINLOG);
static Sys_var_double Sys_optimizer_index_block_copy_cost(
"optimizer_index_block_copy_cost",
"Cost of copying a key block from the cache to intern storage as part of an "
"index scan.",
SESSION_VAR(optimizer_index_block_copy_cost), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, 1), DEFAULT(DEFAULT_INDEX_BLOCK_COPY_COST), NO_MUTEX_GUARD,
NOT_IN_BINLOG);
static Sys_var_double Sys_optimizer_key_next_find_cost(
"optimizer_key_next_find_cost",
"Cost of finding the next key and rowid when using filters.",
SESSION_VAR(optimizer_key_next_find_cost), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, 1), DEFAULT(DEFAULT_KEY_NEXT_FIND_COST), NO_MUTEX_GUARD,
NOT_IN_BINLOG);
static Sys_var_double Sys_optimizer_row_copy_cost(
"optimizer_row_copy_cost",
"Cost of copying a row from the engine or the join cache to the SQL layer.",
SESSION_VAR(optimizer_row_copy_cost), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, 1), DEFAULT(DEFAULT_ROW_COPY_COST), NO_MUTEX_GUARD,
NOT_IN_BINLOG);
static Sys_var_double Sys_optimizer_where_cost(
"optimizer_where_cost",
"Cost of checking the row against the WHERE clause.",
SESSION_VAR(optimizer_where_cost), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, 1), DEFAULT(DEFAULT_WHERE_COST), NO_MUTEX_GUARD,
NOT_IN_BINLOG);
static Sys_var_double Sys_optimizer_key_cmp_cost(
"optimizer_key_compare_cost",
"Cost of checking a key against the end key condition.",
SESSION_VAR(optimizer_key_cmp_cost), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, 1), DEFAULT(DEFAULT_KEY_COMPARE_COST), NO_MUTEX_GUARD,
NOT_IN_BINLOG); NOT_IN_BINLOG);

View File

@@ -5672,12 +5672,14 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
opt_range_condition_rows=0; opt_range_condition_rows=0;
no_cache= false; no_cache= false;
initialize_opt_range_structures(); initialize_opt_range_structures();
/* /*
Update optimizer_cache_cost to ensure that a SET STATEMENT of Update optimizer_costs to ensure that a SET STATEMENT of the
the variable it will work. variables it will work.
*/ */
file->set_optimizer_cache_cost(cache_hit_cost(thd->variables. file->set_optimizer_costs(thd);
optimizer_cache_hit_ratio));
#ifdef HAVE_REPLICATION #ifdef HAVE_REPLICATION
/* used in RBR Triggers */ /* used in RBR Triggers */
master_had_triggers= 0; master_had_triggers= 0;
@@ -10434,6 +10436,14 @@ inline void TABLE::initialize_opt_range_structures()
TRASH_ALLOC(const_key_parts, s->keys * sizeof(*const_key_parts)); TRASH_ALLOC(const_key_parts, s->keys * sizeof(*const_key_parts));
} }
double TABLE::OPT_RANGE::index_only_fetch_cost(THD *thd)
{
return (index_only_cost + (double) rows *
thd->variables.optimizer_key_copy_cost);
}
/* /*
Mark table to be reopened after query Mark table to be reopened after query
*/ */

View File

@@ -1420,10 +1420,7 @@ public:
Cost of fetching keys with index only read and returning them to the Cost of fetching keys with index only read and returning them to the
sql level. sql level.
*/ */
double index_only_fetch_cost() double index_only_fetch_cost(THD *thd);
{
return index_only_cost + (double) rows * KEY_COPY_COST;
}
} *opt_range; } *opt_range;
/* /*
Bitmaps of key parts that =const for the duration of join execution. If Bitmaps of key parts that =const for the duration of join execution. If

View File

@@ -156,7 +156,7 @@ inline double log2_n_fact(double x)
the same length, so each of total_buf_size elements will be added to a sort the same length, so each of total_buf_size elements will be added to a sort
heap with (n_buffers-1) elements. This gives the comparison cost: heap with (n_buffers-1) elements. This gives the comparison cost:
total_buf_elems* log2(n_buffers) / TIME_FOR_COMPARE_ROWID; total_buf_elems* log2(n_buffers) * ROWID_COMPARE_COST;
*/ */
static double get_merge_buffers_cost(uint *buff_elems, uint elem_size, static double get_merge_buffers_cost(uint *buff_elems, uint elem_size,
@@ -171,8 +171,8 @@ static double get_merge_buffers_cost(uint *buff_elems, uint elem_size,
size_t n_buffers= last - first + 1; size_t n_buffers= last - first + 1;
/* Using log2(n)=log(n)/log(2) formula */ /* Using log2(n)=log(n)/log(2) formula */
return 2*((double)total_buf_elems*elem_size) / IO_SIZE + return (2*((double)total_buf_elems*elem_size) / IO_SIZE +
total_buf_elems*log((double) n_buffers) / (compare_factor * M_LN2); total_buf_elems*log((double) n_buffers) * compare_factor / M_LN2);
} }
@@ -327,7 +327,7 @@ double Unique::get_use_cost(uint *buffer, size_t nkeys, uint key_size,
result= 2*log2_n_fact(last_tree_elems + 1.0); result= 2*log2_n_fact(last_tree_elems + 1.0);
if (n_full_trees) if (n_full_trees)
result+= n_full_trees * log2_n_fact(max_elements_in_tree + 1.0); result+= n_full_trees * log2_n_fact(max_elements_in_tree + 1.0);
result /= compare_factor; result *= compare_factor;
DBUG_PRINT("info",("unique trees sizes: %u=%u*%u + %u", (uint)nkeys, DBUG_PRINT("info",("unique trees sizes: %u=%u*%u + %u", (uint)nkeys,
(uint)n_full_trees, (uint)n_full_trees,

View File

@@ -75,7 +75,7 @@ public:
inline static double get_search_cost(ulonglong tree_elems, inline static double get_search_cost(ulonglong tree_elems,
double compare_factor) double compare_factor)
{ {
return log((double) tree_elems) / (compare_factor * M_LN2); return log((double) tree_elems) * compare_factor / M_LN2;
} }
static double get_use_cost(uint *buffer, size_t nkeys, uint key_size, static double get_use_cost(uint *buffer, size_t nkeys, uint key_size,