Commit b6a5d3e8 authored by Sergei Petrunia's avatar Sergei Petrunia

MDEV-9750: Quick memory exhaustion with 'extended_keys=on', Part #4

Part #4:
- Fix comment
- Make tree pruning visible in the optimizer trace.
parent 00675d25
...@@ -3193,8 +3193,8 @@ kp4 in (1,2,3,4,5,6,7,8,9,10) ...@@ -3193,8 +3193,8 @@ kp4 in (1,2,3,4,5,6,7,8,9,10)
; ;
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 t1 index key1 key1 20 NULL 3 Using where; Using index 1 SIMPLE t1 index key1 key1 20 NULL 3 Using where; Using index
set @json= (select json_detailed(JSON_EXTRACT(trace, '$**.range_scan_alternatives')) set @trace= (select trace from information_schema.optimizer_trace);
from information_schema.optimizer_trace); set @json= json_detailed(json_extract(@trace, '$**.range_scan_alternatives'));
select left(@json, 500); select left(@json, 500);
left(@json, 500) left(@json, 500)
[ [
...@@ -3216,6 +3216,41 @@ left(@json, 500) ...@@ -3216,6 +3216,41 @@ left(@json, 500)
"(9) <= (kp1) <= (9)", "(9) <= (kp1) <= (9)",
"(10) <= (kp1) <= (10)" "(10) <= (kp1) <= (10)"
set @json= json_detailed(json_extract(@trace, '$**.setup_range_conditions'));
select left(@json, 2500);
left(@json, 2500)
[
[
{
"enforce_sel_arg_weight_limit":
{
"index": "key1",
"old_weight": 110,
"new_weight": 10
}
},
{
"enforce_sel_arg_weight_limit":
{
"index": "key1",
"old_weight": 110,
"new_weight": 10
}
},
{
"enforce_sel_arg_weight_limit":
{
"index": "key1",
"old_weight": 110,
"new_weight": 10
}
}
]
]
## Repeat the above with a bit higher max_weight: ## Repeat the above with a bit higher max_weight:
set @tmp9750_weight=@@optimizer_max_sel_arg_weight; set @tmp9750_weight=@@optimizer_max_sel_arg_weight;
set optimizer_max_sel_arg_weight=120; set optimizer_max_sel_arg_weight=120;
......
...@@ -2161,10 +2161,13 @@ explain select * from t1 where ...@@ -2161,10 +2161,13 @@ explain select * from t1 where
kp3 in (1,2,3,4,5,6,7,8,9,10) and kp3 in (1,2,3,4,5,6,7,8,9,10) and
kp4 in (1,2,3,4,5,6,7,8,9,10) kp4 in (1,2,3,4,5,6,7,8,9,10)
; ;
set @json= (select json_detailed(JSON_EXTRACT(trace, '$**.range_scan_alternatives')) set @trace= (select trace from information_schema.optimizer_trace);
from information_schema.optimizer_trace); set @json= json_detailed(json_extract(@trace, '$**.range_scan_alternatives'));
select left(@json, 500); select left(@json, 500);
set @json= json_detailed(json_extract(@trace, '$**.setup_range_conditions'));
select left(@json, 2500);
--echo ## Repeat the above with a bit higher max_weight: --echo ## Repeat the above with a bit higher max_weight:
set @tmp9750_weight=@@optimizer_max_sel_arg_weight; set @tmp9750_weight=@@optimizer_max_sel_arg_weight;
set optimizer_max_sel_arg_weight=120; set optimizer_max_sel_arg_weight=120;
......
...@@ -399,9 +399,9 @@ static SEL_ARG *key_or(RANGE_OPT_PARAM *param, ...@@ -399,9 +399,9 @@ static SEL_ARG *key_or(RANGE_OPT_PARAM *param,
static SEL_ARG *key_and(RANGE_OPT_PARAM *param, static SEL_ARG *key_and(RANGE_OPT_PARAM *param,
SEL_ARG *key1, SEL_ARG *key2, SEL_ARG *key1, SEL_ARG *key2,
uint clone_flag); uint clone_flag);
static SEL_ARG *key_or_with_limit(RANGE_OPT_PARAM *param, static SEL_ARG *key_or_with_limit(RANGE_OPT_PARAM *param, uint keyno,
SEL_ARG *key1, SEL_ARG *key2); SEL_ARG *key1, SEL_ARG *key2);
static SEL_ARG *key_and_with_limit(RANGE_OPT_PARAM *param, static SEL_ARG *key_and_with_limit(RANGE_OPT_PARAM *param, uint keyno,
SEL_ARG *key1, SEL_ARG *key2, SEL_ARG *key1, SEL_ARG *key2,
uint clone_flag); uint clone_flag);
static bool get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1); static bool get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1);
...@@ -415,7 +415,9 @@ static bool null_part_in_key(KEY_PART *key_part, const uchar *key, ...@@ -415,7 +415,9 @@ static bool null_part_in_key(KEY_PART *key_part, const uchar *key,
uint length); uint length);
static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts); static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts);
static SEL_ARG *enforce_sel_arg_weight_limit(THD *thd, SEL_ARG *sel_arg); static
SEL_ARG *enforce_sel_arg_weight_limit(RANGE_OPT_PARAM *param, uint keyno,
SEL_ARG *sel_arg);
#include "opt_range_mrr.cc" #include "opt_range_mrr.cc"
...@@ -713,7 +715,8 @@ int SEL_IMERGE::or_sel_tree_with_checks(RANGE_OPT_PARAM *param, ...@@ -713,7 +715,8 @@ int SEL_IMERGE::or_sel_tree_with_checks(RANGE_OPT_PARAM *param,
SEL_ARG *key1= (*or_tree)->keys[key_no]; SEL_ARG *key1= (*or_tree)->keys[key_no];
SEL_ARG *key2= tree->keys[key_no]; SEL_ARG *key2= tree->keys[key_no];
key2->incr_refs(); key2->incr_refs();
if ((result->keys[key_no]= key_or_with_limit(param, key1, key2))) if ((result->keys[key_no]= key_or_with_limit(param, key_no, key1,
key2)))
{ {
result_keys.set_bit(key_no); result_keys.set_bit(key_no);
...@@ -5440,7 +5443,7 @@ TABLE_READ_PLAN *merge_same_index_scans(PARAM *param, SEL_IMERGE *imerge, ...@@ -5440,7 +5443,7 @@ TABLE_READ_PLAN *merge_same_index_scans(PARAM *param, SEL_IMERGE *imerge,
if ((*tree)->keys[key_idx]) if ((*tree)->keys[key_idx])
(*tree)->keys[key_idx]->incr_refs(); (*tree)->keys[key_idx]->incr_refs();
if (((*changed_tree)->keys[key_idx]= if (((*changed_tree)->keys[key_idx]=
key_or_with_limit(param, key, (*tree)->keys[key_idx]))) key_or_with_limit(param, key_idx, key, (*tree)->keys[key_idx])))
(*changed_tree)->keys_map.set_bit(key_idx); (*changed_tree)->keys_map.set_bit(key_idx);
*tree= NULL; *tree= NULL;
removed_cnt++; removed_cnt++;
...@@ -9102,7 +9105,8 @@ int and_range_trees(RANGE_OPT_PARAM *param, SEL_TREE *tree1, SEL_TREE *tree2, ...@@ -9102,7 +9105,8 @@ int and_range_trees(RANGE_OPT_PARAM *param, SEL_TREE *tree1, SEL_TREE *tree2,
key2->incr_refs(); key2->incr_refs();
} }
SEL_ARG *key; SEL_ARG *key;
if ((result->keys[key_no]= key =key_and_with_limit(param, key1, key2, flag))) if ((result->keys[key_no]= key= key_and_with_limit(param, key_no,
key1, key2, flag)))
{ {
if (key && key->type == SEL_ARG::IMPOSSIBLE) if (key && key->type == SEL_ARG::IMPOSSIBLE)
{ {
...@@ -9645,7 +9649,7 @@ tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) ...@@ -9645,7 +9649,7 @@ tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
key1->incr_refs(); key1->incr_refs();
key2->incr_refs(); key2->incr_refs();
} }
if ((result->keys[key_no]= key_or_with_limit(param, key1, key2))) if ((result->keys[key_no]= key_or_with_limit(param, key_no, key1, key2)))
result->keys_map.set_bit(key_no); result->keys_map.set_bit(key_no);
} }
result->type= tree1->type; result->type= tree1->type;
...@@ -9968,11 +9972,12 @@ uint SEL_ARG::verify_weight() ...@@ -9968,11 +9972,12 @@ uint SEL_ARG::verify_weight()
} }
#endif #endif
static SEL_ARG *key_or_with_limit(RANGE_OPT_PARAM *param, static
SEL_ARG *key1, SEL_ARG *key2) SEL_ARG *key_or_with_limit(RANGE_OPT_PARAM *param, uint keyno,
SEL_ARG *key1, SEL_ARG *key2)
{ {
SEL_ARG *res= enforce_sel_arg_weight_limit(param->thd, key_or(param, SEL_ARG *res= key_or(param, key1, key2);
key1, key2)); res= enforce_sel_arg_weight_limit(param, keyno, res);
#ifndef DBUG_OFF #ifndef DBUG_OFF
if (res) if (res)
res->verify_weight(); res->verify_weight();
...@@ -9981,13 +9986,12 @@ static SEL_ARG *key_or_with_limit(RANGE_OPT_PARAM *param, ...@@ -9981,13 +9986,12 @@ static SEL_ARG *key_or_with_limit(RANGE_OPT_PARAM *param,
} }
static SEL_ARG *key_and_with_limit(RANGE_OPT_PARAM *param, static
SEL_ARG *key1, SEL_ARG *key2, SEL_ARG *key_and_with_limit(RANGE_OPT_PARAM *param, uint keyno,
uint clone_flag) SEL_ARG *key1, SEL_ARG *key2, uint clone_flag)
{ {
SEL_ARG *res= enforce_sel_arg_weight_limit(param->thd, key_and(param, key1, SEL_ARG *res= key_and(param, key1, key2, clone_flag);
key2, res= enforce_sel_arg_weight_limit(param, keyno, res);
clone_flag));
#ifndef DBUG_OFF #ifndef DBUG_OFF
if (res) if (res)
res->verify_weight(); res->verify_weight();
...@@ -10773,33 +10777,42 @@ void prune_sel_arg_graph(SEL_ARG *sel_arg, uint max_part) ...@@ -10773,33 +10777,42 @@ void prune_sel_arg_graph(SEL_ARG *sel_arg, uint max_part)
limit. limit.
*/ */
SEL_ARG *enforce_sel_arg_weight_limit(THD *thd, SEL_ARG *sel_arg) SEL_ARG *enforce_sel_arg_weight_limit(RANGE_OPT_PARAM *param, uint keyno,
SEL_ARG *sel_arg)
{ {
if (!sel_arg || sel_arg->type != SEL_ARG::KEY_RANGE || if (!sel_arg || sel_arg->type != SEL_ARG::KEY_RANGE ||
!thd->variables.optimizer_max_sel_arg_weight) !param->thd->variables.optimizer_max_sel_arg_weight)
return sel_arg; return sel_arg;
uint weight1= sel_arg->weight;
while (1) while (1)
{ {
if (sel_arg->weight <= thd->variables.optimizer_max_sel_arg_weight) if (sel_arg->weight <= param->thd->variables.optimizer_max_sel_arg_weight)
return sel_arg; break;
uint max_part= sel_arg->get_max_key_part(); uint max_part= sel_arg->get_max_key_part();
if (max_part == sel_arg->part) if (max_part == sel_arg->part)
return NULL; {
sel_arg= NULL;
#ifdef EXTRA_DEBUG break;
uint weight1= sel_arg->weight; }
#endif
max_part--; max_part--;
prune_sel_arg_graph(sel_arg, max_part); prune_sel_arg_graph(sel_arg, max_part);
}
#ifdef EXTRA_DEBUG uint weight2= sel_arg? sel_arg->weight : 0;
DBUG_PRINT("info", ("enforce_sel_arg_weight_limit: %d->%d", weight1,
sel_arg->weight)); if (weight2 != weight1)
#endif {
Json_writer_object wrapper(param->thd);
Json_writer_object obj(param->thd, "enforce_sel_arg_weight_limit");
obj.add("index", param->table->key_info[param->real_keynr[keyno]].name);
obj.add("old_weight", (longlong)weight1);
obj.add("new_weight", (longlong)weight2);
} }
return sel_arg;
} }
......
...@@ -228,36 +228,43 @@ class RANGE_OPT_PARAM; ...@@ -228,36 +228,43 @@ class RANGE_OPT_PARAM;
A SEL_ARG graph has a property we call weight, and we define it as follows: A SEL_ARG graph has a property we call weight, and we define it as follows:
<definition>
If the SEL_ARG graph does not have any node with multiple incoming If the SEL_ARG graph does not have any node with multiple incoming
next_key_part edges, then its weight is the number of SEL_ARG objects used. next_key_part edges, then its weight is the number of SEL_ARG objects used.
If there is a node with multiple next_key_part edges, clone the node (and If there is a node with multiple incoming next_key_part edges, clone that
the nodes connected via prev/next links to it) and redirect one of the node, (and the nodes connected to it via prev/next links) and redirect one
incoming next_key_part to the clone. (If the node has "peer" nodes of the incoming next_key_part edges to the clone.
connected to it via prev/next links, they will have to be cloned as well)
Repeat this until we get a graph without multiple next_key_part edges Continue with cloning until we get a graph that has no nodes with multiple
coming into the same node. Then, the number of SEL_ARG objects in the incoming next_key_part edges. Then, the number of SEL_ARG objects in the
graph is the weight. graph is the weight of the original graph.
</definition>
Example: Example:
kp1 $ kp2 $ kp3
$ $
| +-------+ $ $ | +-------+ $ $
\->| kp1=2 |--$--------------$-+ \->| kp1=2 |--$--------------$-+
+-------+ $ $ | +--------+ +-------+ $ $ | +--------+
| $ $ ==>| kp3=11 | | $ $ ==>| kp3=11 |
+-------+ $ $ | +--------+ +-------+ $ $ | +--------+
| kp1=3 |--$--------------$-+ | | kp1>3 |--$--------------$-+ |
+-------+ $ $ +--------+ +-------+ $ $ +--------+
$ $ | kp3=14 | $ $ | kp3=14 |
$ $ +--------+ $ $ +--------+
$ $ |
$ $ +--------+
$ $ | kp3=14 |
$ $ +--------+
Here, the weight is 2 + 2*2=6. Here, the weight is 2 + 2*3=8.
The rationale behind the weight is: The rationale behind using this definition of weight is:
- it has the same order-of-magnitude as the number of ranges that the - it has the same order-of-magnitude as the number of ranges that the
SEL_ARG graph is describing, SEL_ARG graph is describing,
- it is a lot easier to compute, - it is a lot easier to compute than computing the number of ranges,
- it can be updated incrementally when performing AND/OR operations on - it can be updated incrementally when performing AND/OR operations on
parts of the graph. parts of the graph.
*/ */
......
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