[MDEV-9750] Quick memory exhaustion with 'extended_keys=on' on queries having multiple 'IN'/'NOT IN' using InnoDB Created: 2016-03-17 Updated: 2022-08-12 Resolved: 2021-01-29 |
|
| Status: | Closed |
| Project: | MariaDB Server |
| Component/s: | Optimizer |
| Affects Version/s: | 10.0.24, 5.5, 10.0, 10.1, 10.2 |
| Fix Version/s: | 10.5.9 |
| Type: | Bug | Priority: | Blocker |
| Reporter: | Jean Weisbuch | Assignee: | Sergei Petrunia |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | range-optimizer | ||
| Environment: |
Debian Wheezy and Jessie amd64 |
||
| Attachments: |
|
||||||||||||||||||||||||||||
| Issue Links: |
|
||||||||||||||||||||||||||||
| Sprint: | 5.5.50 | ||||||||||||||||||||||||||||
| Description |
|
Running the attached query (either with EXPLAIN or by simply doing the SELECT) on the following empty table will exhaust system memory in seconds(uses more than 10G of memory in less than 2 minutes), killing the query even less than a second after having executed it can take up to 30 seconds to finish (the query will be in "Killed" command and "statistics" state on the Processlist) while still keep eating up more memory. Tested on both Wheezy and Jessie packaged versions of 10.0.24 with the same result, it didnt seemed to happen on 5.5.48 as i hit the bug only since i upgraded. The bug doesnt happens when :
Using "smallint" for both columns result in the query to be faster to kill but still not ending while eating memory. –
|
| Comments |
| Comment by Elena Stepanova [ 2016-03-17 ] | |||||||||||||||||||||||
|
Thanks for the report and test case. | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2016-03-18 ] | |||||||||||||||||||||||
|
The query has form:
| |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2016-06-07 ] | |||||||||||||||||||||||
|
Similar issues. The were two patches pushed already to 10.1 for | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-10-15 ] | |||||||||||||||||||||||
|
Reproducible on recent 10.4 (and I believe it will be on 10.5, too) I've added ignore index (PRIMARY) into memoryexhaust.sql and it's stillreproducible. Debugging one can see that PARAM::alloced_sel_args=0, but the number of SEL_ARG::SEL_ARG calls (total for all signatures) is over 100M and the optimizer is still running. | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-10-15 ] | |||||||||||||||||||||||
|
Ok
.
The above has about 5K constants.
4K constants in total, 254 pcs in each NOT IN clause except the last one. 17 | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-10-15 ] | |||||||||||||||||||||||
|
My theory about what's going on here. First, the optimizer produces a SEL_ARG graph representing a range list on secondary_id. Then, the same happens for the first NOT IN:
This produces a range list with almost-touching ranges:
When AND-ing these two, we get this kind of graph: red color shows the SEL_ARG::next_key_part edges. The tree representing the second key part is shared, so the memory consumption is not an issue, yet. Then, we try to AND this with another graph: And here, the issues start. The same goes on for each range on the sec[ondary]_id. This means we will create 4K * 250 = 1M SEL_ARG objects. When processing the next NOT-IN clause, we will need to fork 500 element tree (since NOT-IN lists do not have common elements). 4K * 500 = 2M In total there are 16 NOT IN clauses so we'll need to create
128 billion SEL_ARG objects. | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-10-15 ] | |||||||||||||||||||||||
|
... the first idea is to make sharing more aggressive. If we were computing a key_or(ptr1, ptr2) where ptr1 and ptr2 were shared, and got ptr3, maybe the next operation which also computes key_or(ptr1, ptr2) should just grab ptr3 as result (and increase its share counter). This might solve the memory issue, but would it make the query speed adequate? | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-10-15 ] | |||||||||||||||||||||||
|
I have re-ordered parts of the WHERE clause so that like so:
and now I see that get_mm_tree() part of the range optimizer computes fairly quickly (with total # of SEL_ARG objects constructed = 46716). But then the optimizer is stuck in doing records_in_range calls. One can expect in total 5K * 4K = 20M calls. | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-10-15 ] | |||||||||||||||||||||||
|
memoryexhaust-order2.sql | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-10-15 ] | |||||||||||||||||||||||
|
I think the right approach is to modify the optimizer to not produce SEL_ARG graphs that represent > N_MAX ranges, even if the graph itself has [much] less than N_MAX nodes. Not producing should be achieved by discarding parts of the graph that represent bigger key parts. | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-10-16 ] | |||||||||||||||||||||||
|
Note to self: a bug in this part of code have been fixed recently, it is | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-10-20 ] | |||||||||||||||||||||||
|
Attempts on MySQL-8:
and one of the warnings is:
MySQL8 implements the check for range_optimizer_max_mem_size using MEM_ROOT::m_max_capacity.
| |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-10-20 ] | |||||||||||||||||||||||
|
A suggestion for a fix: Definitions:SEL_ARG tree - a set of SEL_ARG structures describing the same key part and connected via prev/next/left/right links. SEL_ARG graph - same as above but not limited to the same keypart. SEL_ARGs can be connected via next_key_part link, too. SEL_ARG graph weightLet each SEL_ARG graph have a "weight". Weight is a metric that's similar to "number of ranges the SEL_ARG graph would generate" but not exactly that as it ignores the limitations that range generation imposes. These limitations include:
An alternative definition of weight: take a SEL_ARG graph, and make sure there are no "shared trees". That is, there are no nodes with multiple incoming next_key_part links. If the graph has is such nodes, clone them. Another possible way to define the weight is: let it be the total number of nodes that SEL_ARG graph would have if sharing is eliminated. Using the weight.Don't construct overweight graphs. When OR-ing or AND-ing SEL_ARG graphs, compute the weight of the result. If the weight exceeds MAX_WEIGHT: Note: when removing SEL_ARG trees, we don't need to care if the node referring to that tree is shared or not. The criteria for removal remains the same regardless of the path that we have arrived to the next_key_part link to the tree we're removing. | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-10-24 ] | |||||||||||||||||||||||
|
Note: SEL_ARG::max_part_no is not actually the number of the biggest key part Example:
| |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-11-03 ] | |||||||||||||||||||||||
|
Pushed dbug_print_sel_arg patch into 10.2: https://github.com/MariaDB/server/commit/a593e03d58f922a99ba49de1bec6810fc7e9874f | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-11-05 ] | |||||||||||||||||||||||
|
Patch: http://lists.askmonty.org/pipermail/commits/2020-November/014349.html . | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-11-05 ] | |||||||||||||||||||||||
|
Igor, please review. | |||||||||||||||||||||||
| Comment by Igor Babaev [ 2020-11-07 ] | |||||||||||||||||||||||
|
Sergei, | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-11-08 ] | |||||||||||||||||||||||
|
Hi igor,
Nitpick: No, they would not, as SEL_ARGs on different indexes are not shared. I've just debugged this:
and the execution hit SEL_ARG::SEL_ARG 3 times.
But your question is valid, can it occur that the tree that is pruned is shared, and what should be done in that case. Let me get back on this. | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-11-09 ] | |||||||||||||||||||||||
|
.. well, the answer is that yes, it is possible. I don't think doing pruning in a shared sub-graph causes harm , but it makes the semantics of the operation complex. igor Please find the variant of the patch that only does pruning for complete graphs: | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-11-11 ] | |||||||||||||||||||||||
|
A patch with more comments: http://lists.askmonty.org/pipermail/commits/2020-November/014355.html | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2020-12-15 ] | |||||||||||||||||||||||
|
Review input:
| |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2021-01-20 ] | |||||||||||||||||||||||
|
Patch addressing almost all of the input: | |||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2021-01-26 ] | |||||||||||||||||||||||
|
Branch on github https://github.com/MariaDB/server/tree/bb-10.4-mdev9750-v2 |