Bug #29582 huge memory consumption with union, subselect, joins
Submitted: 5 Jul 2007 18:57 Modified: 3 Aug 2007 2:29
Reporter: Shane Bester (Platinum Quality Contributor) Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S1 (Critical)
Version:5.0.42, 5.0.46BK OS:Any
Assigned to: Sergey Petrunya CPU Architecture:Any
Tags: bfsm_2007_07_19, Leak, Memory, UNION

[5 Jul 2007 18:57] Shane Bester
Description:
A query in this form uses a few hundred MB of ram during execution.
So, for a 32-bit server, you'll be out of memory if two clients execute this at the same time:

I narrowed down the culprit to be the UNION in the derived table.  Without the UNION, the memory doesn't get wasted as much.

The general form of the query is:

SELECT  SQL_CALC_FOUND_ROWS 
        
        COUNT(DISTINCT ...),
        IF(SUM(DISTINCT ...) IS NOT NULL, SUM(DISTINCT ...), 0), 
        IF(... IS NOT NULL, true, false)
FROM
        t1
        INNER JOIN ...
        INNER JOIN ...
        LEFT OUTER JOIN
        (
                SELECT DISTINCT ...
                FROM 
                       ...
                WHERE 
                        ...=5 AND ...=1
        ) ... ON ...
        
WHERE 
(
        ...=true OR 
        ... IS NULL
)  AND 

... IN 
(
        SELECT ....
        FROM
                .....
                INNER JOIN ... ON .....
                INNER JOIN ... ON .....
        WHERE 
                ....=1 AND 
                (..., ...)=(5, 1) AND 
                ...=0 
                
        UNION
        
        SELECT .....
        FROM 
                .....
                INNER JOIN ... ON ......
                INNER JOIN ... ON ......
                INNER JOIN ... ON ...... 
        WHERE 
                ...=2 AND 
                ...=1 AND 
                ...=5
        
)  AND 
...>'2007-02-02 11:09:13.75' 
GROUP BY 
        ....
ORDER BY 
        ... DESC 
LIMIT 20

How to repeat:
simpler testcase will be uploaded later.

Suggested fix:
.
[5 Jul 2007 21:14] MySQL Verification Team
the query above takes 20 seconds on a release build of mysqld, but >10 minutes on a full debug build.  I don't know why that would be..
[18 Jul 2007 15:50] Sergey Petrunya
Ok, the memory consumption is caused by this call:

   mysql_select( various fake_select_lex's members )

this invokes JOIN::optimize which allocates memory on the execution MEM_ROOT. When the union is within the subquery, this causes a total of O(#subselect-executions) memory to be allocated.

The fix seems to be to replace the generic mysql_select() call with 

  if (optimization already done) 
  {
     join->exec();
     // some join->cleanup()-like call here
  }

which is known not to consume memory.
[25 Jul 2007 8:11] Sergey Petrunya
The ETA is 2007-07-27.
[30 Jul 2007 13:13] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/31817

ChangeSet@1.2532, 2007-07-30 17:12:34+04:00, sergefp@mysql.com +3 -0
  BUG#29582: huge memory consumption with union, subselect, joins:
  - Don't call mysql_select() several times for the select that enumerates
    the temporary table with the results of the UNION. Making this call for
    every subquery execution caused O(#enumerated-rows-in-the-outer-query) 
    memory allocations.
  - Instead, call join->reinit() and join->exec(), and
    = disable constant table detection for such joins,  
    = provide special handling for table-less constant subqueries.
[30 Jul 2007 13:20] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/31818

ChangeSet@1.2532, 2007-07-30 17:19:49+04:00, sergefp@mysql.com +3 -0
  BUG#29582: huge memory consumption with union, subselect, joins:
  - Don't call mysql_select() several times for the select that enumerates
    the temporary table with the results of the UNION. Making this call for
    every subquery execution caused O(#enumerated-rows-in-the-outer-query) 
    memory allocations.
  - Instead, call join->reinit() and join->exec(), and
    = disable constant table detection for such joins,  
    = provide special handling for table-less constant subqueries.
[31 Jul 2007 11:19] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/31884

ChangeSet@1.2532, 2007-07-31 15:19:06+04:00, sergefp@mysql.com +3 -0
  BUG#29582: huge memory consumption with union, subselect, joins:
  - Don't call mysql_select() several times for the select that enumerates
    a temporary table with the results of the UNION. Making this call for
    every subquery execution caused O(#enumerated-rows-in-the-outer-query) 
    memory allocations.
  - Instead, call join->reinit() and join->exec(), and
    = disable constant table detection for such joins,  
    = provide special handling for table-less constant subqueries.
[1 Aug 2007 10:15] Sergey Petrunya
Pushed into 5.0-opt at 2007-07-31 13:46:41.
[2 Aug 2007 19:13] Bugs System
Pushed into 5.1.21-beta
[2 Aug 2007 19:15] Bugs System
Pushed into 5.0.48
[3 Aug 2007 2:29] Paul DuBois
Noted in 5.0.48, 5.1.21 changelogs.

Certain statements with unions, subqueries, and joins could
result in huge memory consumption.