10

I have a MySQL database with InnoDB tables summing up over 10 ten GB of data that I want to migrate from MySQL 5.5 to MySQL 5.7. And I have a query that looks a bit like:

SELECT dates.date, count(mySub2.myColumn1), sum(mySub2.myColumn2)
FROM (
    SELECT date
    FROM dates -- just a table containing all possible dates next 5 years
    WHERE date BETWEEN '2016-06-01' AND '2016-09-03'
) AS dates
LEFT JOIN (
    SELECT o.id, time_start, time_end
    FROM order AS o
    INNER JOIN order_items AS oi on oi.order_id = o.id
    WHERE time_start BETWEEN '2016-06-01' AND '2016-09-03'
) AS mySub1 ON dates.date >= mySub1.time_start AND dates.date < mySub1.time_end
LEFT JOIN (
    SELECT o.id, time_start, time_end
    FROM order AS o
    INNER JOIN order_items AS oi on oi.order_id = o.id
    WHERE o.shop_id = 50 AND time_start BETWEEN '2016-06-01' AND '2016-09-03'
) AS mySub2 ON dates.date >= mySub2.time_start AND dates.date < mySub2.time_end
GROUP BY dates.date;

My problem is that this query is performing fast in MySQL 5.5 but extremely slow in MySQL 5.7.

In MySQL 5.5 it is taking over 1 second at first and < 0.001 seconds every recurring execution without restarting MySQL.
In MySQL 5.7 it is taking over 11.5 seconds at first and 1.4 seconds every recurring execution without restarting MySQL.
And the more LEFT JOINs I add to the query, the slower the query becomes in MySQL 5.7.

Both instances now run on the same machine, on the same hard drive and with the same my.ini settings. So it isn't hardware.
The execution plans do differ, though and I don't know what to make from it.

This is the EXPLAIN EXTENDED on MySQL 5.5:

| id | select_type | table      | type  | possible_keys | key         | key_len | ref       | rows  | filtered | extra                           |
|----|-------------|------------|-------|---------------|-------------|---------|-----------|-------|----------|---------------------------------|
| 1  | PRIMARY     | dates      | ALL   |               |             |         |           | 95    | 100.00   | Using temporary; Using filesort |
| 1  | PRIMARY     | <derived2> | ALL   |               |             |         |           | 281   | 100.00   | ''                              |
| 1  | PRIMARY     | <derived3> | ALL   |               |             |         |           | 100   | 100.00   | ''                              |
| 3  | DERIVED     | o          | ref   | xxxxxx        | shop_id_fk  | 4       | ''        | 1736  | 100.00   | ''                              |
| 3  | DERIVED     | oc         | ref   | xxxxx         | order_id_fk | 4       | myDb.o.id | 1     | 100.00   | Using index                     |
| 2  | DERIVED     | o          | range | xxxx          | date_start  | 3       |           | 17938 | 100.00   | Using where                     |
| 2  | DERIVED     | oc         | ref   | xxx           | order_id_fk | 4       | myDb.o.id | 1     | 100.00   | Using where                     |

This is the EXPLAIN EXTENDED on MySQL 5.7:

| id | select_type | table | type   | possible_keys | key         | key_len | ref              | rows | filtered | extra          |
|----|-------------|-------|--------|---------------|-------------|---------|------------------|------|----------|----------------|
| 1  | SIMPLE      | dates | ALL    |               |             |         |                  | 95   | 100.00   | Using filesort |
| 1  | SIMPLE      | oi    | ref    | xxxxxx        | order_id_fk | 4       | const            | 228  | 100.00   |                |
| 1  | SIMPLE      | o     | eq_ref | xxxxx         | PRIMARY     | 4       | myDb.oi.order_id | 1    | 100.00   | Using where    |
| 1  | SIMPLE      | o     | ref    | xxxx          | shop_id_fk  | 4       | const            | 65   | 100.00   | Using where    |
| 1  | SIMPLE      | oi    | ref    | xxx           | order_id_fk | 4       | myDb.o.id        | 1    | 100.00   | Using where    |

I want to understand why the MySQLs treat the same query that much different, and how I can tweak MySQL 5.7 to be faster?
I'm not looking for help on rewriting the query to be faster, as that is something I am already doing on my own.

nl-x
  • 11,762
  • 7
  • 33
  • 61
  • Just to make sure... (1)the queries are exactly the same? (2)the tables, including indexes, are exactly the same? – Uueerdo Jun 09 '16 at 18:57
  • @Uueerdo yes, exactly. At first it was on different machines. But when I encountered this, I installed both MySQL 5.5 and MySQL 5.7 on my laptop, and imported the same dump twice. And I took the same my.ini and made a minimum of changes to it so I could run the two instances at once. So everything is alike. And only then I ran the same query on them. – nl-x Jun 09 '16 at 18:58
  • I've got no clue then. My guess would be optimizations made for more common types of queries negatively affecting this particular (and peculiar) query. – Uueerdo Jun 09 '16 at 19:01
  • Could the query plans be unrelated, and MySQL 5.7 just turning off some features by default, such as some kind of key buffering? – nl-x Jun 09 '16 at 19:04
  • Did you copy the old server's ini file, or go through and match up settings? It is possible the newer version offers additional options that may default to undesirable behavior if omitted when the ini file is overwritten; or may have new options that need disabled to behave like previous versions. If 5.7 were "turning some features off", I would expect those settings to show up in the ini file. – Uueerdo Jun 09 '16 at 19:08
  • @Uueerdo I actually took the ini from the newer server, and copied it to the older one, taking out the lines that were not compatible with the older one. So that shouldn't be it. – nl-x Jun 09 '16 at 19:12
  • I'd look into what those lines are for then. – Uueerdo Jun 09 '16 at 19:14
  • 2
    Optimizer behavior can vary between versions, check variable `optimizer_switch` on each server [5.5](http://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_optimizer_switch) and [5.7](http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_optimizer_switch). – wchiquito Jun 09 '16 at 20:33
  • In your first subquery `mySub1`, try to use `...FROM order AS o FORCE INDEX (date_start) INNER JOIN ...` or `...FROM order AS o FORCE INDEX (date_start) INNER JOIN order_items AS oi on time_start BETWEEN '2016-06-01' AND '2016-09-03' and oi.order_id = o.id ...`. If it doesn't help, could you add the explain for the second one? – Solarflare Jun 09 '16 at 22:36
  • @wchiquito Please post your comment as an answer, so I can accept it. `set optimizer_switch='derived_merge=off';` fixes my problem. This is a new flag that was not present in MySQL 5.5. – nl-x Jun 10 '16 at 07:38
  • Do not worry, what really matters is that their goal was achieved. Thanks. – wchiquito Jun 10 '16 at 08:38
  • If a `time_start` to `time_end` can span multiple days, then `count(mySub2.myColumn1)` will count each 'order' for each day. Did you want that? Or did you want to count each 'order' only once? – Rick James Jun 24 '16 at 14:43
  • `< 0.001 seconds` implies that the Query Cache was in effect. – Rick James Jun 24 '16 at 14:45
  • To me, based on the numbers given by EXPLAIN, it seems like the 5.7 plan should be better than the 5.5 plan. It would be interesting to compare the handler status variables for the two versions. If you do FLUSH STATUS before executing the query and SHOW STATUS LIKE 'handler_read%' after the query, you will see the actual number of rows accessed by the two queries. – Øystein Grøvlen Jun 27 '16 at 07:25

3 Answers3

13

As can be read in the comments, @wchiquito has suggested to look at the optimizer_switch. In here I found that the switch derived_merge could be set to off, to fix this new, and in this specific case undesired, behaviour.

set session optimizer_switch='derived_merge=off'; fixes the problem.
(This can also be done with set global ... or be put in the my.cnf / my.ini)

nl-x
  • 11,762
  • 7
  • 33
  • 61
  • You can read a little more in the following article: [Derived Tables in MySQL 5.7](http://mysqlserverteam.com/derived-tables-in-mysql-5-7/) and [9.2.1.18 Subquery Optimization](https://dev.mysql.com/doc/refman/5.7/en/subquery-optimization.html#derived-table-optimization). – wchiquito Jun 10 '16 at 08:46
  • Thank you very much. I have same situation and your solution is worked. In my case, mysql-5.5 return result after 0.5s and mysql-5.7 - after 1.2s. After disabling derived_merge optimizer and rewriting query from joins to subqueries mysql-5.7 return result after 0.01s – Rodion V Sep 10 '16 at 20:08
  • Thanks @nl-x . Spent an entire day trying to figure out settings why a query that was taking 0.9 seconds on the old MySQL database server was taking 4.5 minutes on the MariaDB server. Turning off the 'derived_merge' flag decreased the time for the MariaDB query to 0.8 seconds. What a difference! I don't understand why this flag is defaulted to ON if the performance hit is so great. Or at least MariaDB should document it better for users switching from MySQL. – Johnny Jan 05 '18 at 08:36
  • Perfecto ! Excellent one – KrishCdbry Jan 07 '19 at 09:51
  • Nice one, had the same problem after upgrading to 5.7. if these were set to off by default on 5.7 it would have avoided all the stress – Santosh Pillai Apr 02 '19 at 09:45
  • @SantoshPillai was there need for mysql restart? I have this issue right now and i cant fix it . . On test MariaDB 10.1.37 works faster but on MySQL 5.7.23 works really slow – Ingus Apr 05 '19 at 07:58
  • @Ingus Have you tried tweaking any of the other optimizer switches? – nl-x Apr 05 '19 at 08:45
  • @nl-x not yet! We just discovered that mysql uses 100% CPU. Any thoughts for that? – Ingus Apr 05 '19 at 08:49
  • 1
    @Ingus I have added this to my.cnf and restarted mysql. Changes --- [mysqld] sql_mode='' optimizer_switch='derived_merge=off,duplicateweedout=off' – Santosh Pillai Apr 05 '19 at 11:19
  • @Ingus have you also carried out mysql_upgrade to upgrade tables? – Santosh Pillai Apr 05 '19 at 11:24
  • @SantoshPillai What you mean with mysql_upgrade ? Version upgrade? If so i upgraded to 5.7.25 and result are the same – Ingus Apr 05 '19 at 12:37
  • @lingus run command mysql_upgrade https://dev.mysql.com/doc/refman/5.6/en/mysql-upgrade.html – Santosh Pillai Apr 05 '19 at 16:36
1

Building and maintaining a "Summary Table" would make this query run much faster than even 1 second.

Such a table would probably include shop_id, date, and some count.

More on summary tables.

Rick James
  • 135,179
  • 13
  • 127
  • 222
0

I too faced slow query execution issue after migrating to mysql 5.7 and in my case, even setting session optimizer_switch to 'derived_merge=off'; didn't help.

Then, I followed this link: https://www.saotn.org/mysql-innodb-performance-improvement/ and the query's speed became normal.

To be specific my change was just setting these four parameters in my.ini as described in the link:

innodb_buffer_pool_size

innodb_buffer_pool_instances

innodb_write_io_threads

innodb_read_io_threads

zed101
  • 193
  • 1
  • 2
  • 10