[MDEV-4240] mariadb 5.3.12 using more memory than MySQL 5.1 for an inefficient query Created: 2013-03-04 Updated: 2013-04-03 Resolved: 2013-04-03 |
|
| Status: | Closed |
| Project: | MariaDB Server |
| Component/s: | None |
| Affects Version/s: | 5.3.12 |
| Fix Version/s: | 5.3.13 |
| Type: | Bug | Priority: | Minor |
| Reporter: | Peter (Stig) Edwards | Assignee: | Sergei Petrunia |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | optimizer | ||
| Environment: |
RedHat EL6 using mariadb-5.3.12-Linux-x86_64.tar.gz |
||
| Description |
|
Hello and thank you for mariadb, I noticed mariadb 5.3.12 using a lot more memory than MySQL 5.1.48 for a very inefficient query (that should never get near production):
table_a has 4,834,708 rows, The query:
MySQL 5.1.48 takes a while but does return 4,099,991 rows with:
mariadb 5.3.12 hits a 20 Gigabyte virtual memory ulimit, RSS is around 16G.
I was using the following when reproducing:
Thank you. |
| Comments |
| Comment by Peter (Stig) Edwards [ 2013-03-04 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
I sent MDEV4240_resend.tar.gz to ftp.askmonty.org/private, it should be 68679677 bytes (MDEV4240.tar.gz transfer was interrupted). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Peter (Stig) Edwards [ 2013-03-04 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
I just tried 5.3 revision 3632 and see the same behaviour as 5.3.12. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Peter (Stig) Edwards [ 2013-03-04 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
I just tried mysql-5.1.68-linux-x86_64-glibc23 and see the same behaviour as MySQL 5.1.48 (data returned staying under 10 Gigabyte virtual memory limit). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Elena Stepanova [ 2013-03-05 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
>> MySQL 5.1.48 takes a while "a while" is one way to put it, it took almost 6 hours in my VM, but it did work at the end, even with 6GB memory + 2 GB swap: The problem with 5.3 hitting out-of-memory was also reproducible. Peter, Did you happen to try to scale down the test case a bit, so it would still show the extra memory usage, but would take less time? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Peter (Stig) Edwards [ 2013-03-06 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
I didn't try and scale down the number of rows past the test data I sent, it took a couple of minutes on the physical host I was testing on. Tomorrow I will post the other my.cnf [mysqld] settings that may have helped with my shorter runtime. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Peter (Stig) Edwards [ 2013-03-06 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sorry for the delay, these are some of the settings that might be helping with my runtime (the host is a Dell PowerEdge R620 with 64G of RAM, 32x Xeon @ 2.7GHz, RAID-1 on 2.5" 10K disks with 64MB buffer, under very low load). [mysqld] With MySQL 5.1.68 > pager cat > /dev/null > SELECT table_a.field_d FROM table_a INNER JOIN table_b ON table_a.field_c = SUBSTR(table_b.field_c, 1, 8) OR table_a.field_d = SUBSTR(table_b.field_d, 1, 11); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Elena Stepanova [ 2013-03-08 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
It's still very slow on my machine, even with the provided parameters, apparently the reason is different. MariaDB [test]> select count
----------
---------- MariaDB [test]> select count
----------
---------- > SELECT table_a.field_d FROM table_a INNER JOIN table_b ON table_a.field_c = SUBSTR(table_b.field_c, 1, 8) OR table_a.field_d = SUBSTR(table_b.field_d, 1, 11); ... 5.1 5.3 ... I'm not totally sure that the increased memory usage as such is a bug, it seems fair that a much faster execution time comes at the cost of more resources. What is worrisome however is that I haven't found a way to revert 5.3 to 5.1 behavior – even if I set optimizer_switch to the value of 5.1, the memory consumption is still the same: SET optimizer_switch = REPLACE( @@optimizer_switch, '=on', '=off' ); 350328 rows in set (1 min 15.42 sec) 4277 elenst 20 0 6749m 3.0g 7056 S 100 31.1 1:16.09 mysqld The dumps with smaller data are uploaded at ftp, side by side with the original ones, file name MDEV4240_shorter.tar.gz. I'll pass it to Sergey to decide whether there is a misbehavior here somewhere. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Peter (Stig) Edwards [ 2013-03-20 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Found a host with some more free RAM to test a 30G virtual memory ulimit. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2013-03-29 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There is no way how this query could meaningfully use so much memory. Memory is just wasted somewhere. The query uses "range checked for each record". This optimization does the following: for each record of table_b { } Apparently, each loop iteration gets some memory to be allocated on query's MEM_ROOT. Let's catch who does it: Breakpoint 1, alloc_root (mem_root=0xa337478, length=1816) at my_alloc.c:182
The culprit is: #0 alloc_root (mem_root=0xa337478, length=1816) at my_alloc.c:182 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2013-03-29 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
After I make the above allocation to be done on quick select's mem_root and run (while true; do sleep 2; ps hv --pid=29160 ; done ) PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND The query finishes in 8 min 6.95 sec, on debug build. RSS grows slowly as the query runs, reaching the top of about 1.2G. I am not sure why it is growing, but at least the patch cuts the growth rate. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2013-03-29 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Not directly related to the problem, but I was wondering whether the provided query plan is an efficient one. Increasing join_buffer_size will make mysql do a cross-join in memory: set join_buffer_size=120*1000*1000; MariaDB [j60]> explain SELECT table_a.field_d FROM table_a INNER JOIN table_b ON table_a.field_c = SUBSTR(table_b.field_c, 1, 8) OR table_a.field_d = SUBSTR(table_b.field_d, 1, 11);
---
---
I re-wrote it as UNION, hoping two ref accesses will be faster. nope: MariaDB [j60]> explain SELECT table_a.field_d FROM table_a INNER JOIN table_b ON table_a.field_c = SUBSTR(table_b.field_c, 1, 8) union SELECT table_a.field_d FROM table_a INNER JOIN table_b ON table_a.field_d = SUBSTR(table_b.field_d, 1, 11);
---
--- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2013-03-29 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
All of the above execution times are from a debug build. Release one should be faster. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2013-03-29 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
The patch: === modified file 'sql/opt_range.cc' } This patch needs to be improved - there are other kinds of quick selects that may call handler::clone(). They all should make sure that thd->mem_root is pointing to quick select's mem_root. If thd->mem_root is a statement-execution-mem_root, then that mem_root will accumulate a lot of data. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2013-04-01 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Committed another variant of the patch. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2013-04-02 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sanja, please reassign back when review is done. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Oleksandr Byelkin [ 2013-04-03 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
OK to push | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Sergei Petrunia [ 2013-04-03 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Fix pushed into 5.3 |