Chris Muench
Chris Muench

Reputation: 18318

Query on same database runs in .4 seconds in mariadb and 83 seconds in mysql 5.6,5.7 and 8

I have the following query that runs really slow on mysql (83 seconds) but really fast on mariadb (.4 seconds).

I verified the data database has the same indexes and data. Maria Db server has less cpu (1VCPU), memory (2gb)

Mysql servers have 8 - 32GB ram and full quad core processors (tried 5.6,5.7, and 8.0 with similar results).

The phppos_inventory table has ~170000 rows and the phppos_items table has ~3000 rows

Here is the query and the tables and explains

SELECT /*+ SEMIJOIN(@subq MATERIALIZATION) */ SQL_CALC_FOUND_ROWS 
1                                            AS _h, 
`phppos_location_items`.`location_id`        AS `location_id`, 
`phppos_items`.`item_id`, 
`phppos_items`.`name`, 
`phppos_categories`.`id`                     AS `category_id`, 
`phppos_categories`.`name`                   AS `category`, 
`location`, 
`company_name`, 
`phppos_items`.`item_number`, 
`size`, 
`product_id`, 
Coalesce(phppos_location_item_variations.cost_price, 
phppos_item_variations.cost_price, phppos_location_items.cost_price, 
phppos_items.cost_price, 0)                  AS cost_price, 
Coalesce(phppos_location_item_variations.unit_price, 
phppos_item_variations.unit_price, phppos_location_items.unit_price, 
phppos_items.unit_price, 0)                  AS unit_price, 
Sum(Coalesce(inv.trans_current_quantity, 0)) AS quantity, 
Coalesce(phppos_location_item_variations.reorder_level, 
phppos_item_variations.reorder_level, phppos_location_items.reorder_level, 
phppos_items.reorder_level)                  AS reorder_level, 
Coalesce(phppos_location_item_variations.replenish_level, 
phppos_item_variations.replenish_level, phppos_location_items.replenish_level, 
phppos_items.replenish_level)                AS replenish_level, 
description 
FROM   `phppos_inventory` `inv` 
       LEFT JOIN `phppos_items` 
              ON `phppos_items`.`item_id` = `inv`.`trans_items` 
       LEFT JOIN `phppos_location_items` 
              ON `phppos_location_items`.`item_id` = `phppos_items`.`item_id` 
                 AND `phppos_location_items`.`location_id` = `inv`.`location_id` 
       LEFT JOIN `phppos_item_variations` 
              ON `phppos_items`.`item_id` = `phppos_item_variations`.`item_id` 
                 AND `phppos_item_variations`.`id` = `inv`.`item_variation_id` 
                 AND `phppos_item_variations`.`deleted` = 0 
       LEFT JOIN `phppos_location_item_variations` 
              ON `phppos_location_item_variations`.`item_variation_id` = 
                           `phppos_item_variations`.`id` 
                 AND `phppos_location_item_variations`.`location_id` = 
                     `inv`.`location_id` 
       LEFT OUTER JOIN `phppos_suppliers` 
                    ON `phppos_items`.`supplier_id` = 
                       `phppos_suppliers`.`person_id` 
       LEFT OUTER JOIN `phppos_categories` 
                    ON `phppos_items`.`category_id` = `phppos_categories`.`id` 
WHERE  inv.trans_id = (SELECT Max(inv1.trans_id) 
                       FROM   phppos_inventory inv1 
                       WHERE  inv1.trans_items = inv.trans_items 
                              AND ( inv1.item_variation_id = 
                                    phppos_item_variations.id 
                                     OR phppos_item_variations.id IS NULL ) 
                              AND inv1.location_id = inv.location_id 
                              AND inv1.trans_date < '2019-12-31 23:59:59') 
       AND inv.location_id IN( 1 ) 
       AND `phppos_items`.`system_item` = 0 
       AND `phppos_items`.`deleted` = 0 
       AND `is_service` != 1 
GROUP  BY `phppos_items`.`item_id` 
LIMIT  20 

Explain mysql (slighly different than maria db but I tried use index to match the execution plan and still was slow)

+------------------------------------------+-------+----------+------------------------------------+
| id | select_type        | table                           | partitions | type   | possible_keys                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | key                     | key_len | ref                                     | rows  | filtered | Extra                              |
+----+--------------------+---------------------------------+------------+--------+------------------------------+-------+----------+------------------------------------+
|  1 | PRIMARY            | phppos_items                    | NULL       | ref    | PRIMARY,item_number,product_id,phppos_items_ibfk_1,deleted,phppos_items_ibfk_3,phppos_items_ibfk_4,phppos_items_ibfk_5,description,size,reorder_level,cost_price,unit_price,promo_price,last_modified,name,phppos_items_ibfk_6,deleted_system_item,custom_field_1_value,custom_field_2_value,custom_field_3_value,custom_field_4_value,custom_field_5_value,custom_field_6_value,custom_field_7_value,custom_field_8_value,custom_field_9_value,custom_field_10_value,verify_age,phppos_items_ibfk_7,item_inactive_index,tags,full_search,name_search,item_number_search,product_id_search,description_search,size_search,custom_field_1_value_search,custom_field_2_value_search,custom_field_3_value_search,custom_field_4_value_search,custom_field_5_value_search,custom_field_6_value_search,custom_field_7_value_search,custom_field_8_value_search,custom_field_9_value_search,custom_field_10_value_search | deleted                 | 4       | const                                   | 21188 |     9.00 | Using index condition; Using where |
|  1 | PRIMARY            | inv                             | NULL       | ref    | phppos_inventory_ibfk_1,location_id,phppos_inventory_custom                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | phppos_inventory_custom | 8       | pos.phppos_items.item_id,const          |     3 |   100.00 | NULL                               |
|  1 | PRIMARY            | phppos_location_items           | NULL       | eq_ref | PRIMARY,phppos_location_items_ibfk_2                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | PRIMARY                 | 8       | const,pos.phppos_items.item_id          |     1 |   100.00 | NULL                               |
|  1 | PRIMARY            | phppos_item_variations          | NULL       | eq_ref | PRIMARY,phppos_item_variations_ibfk_1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | PRIMARY                 | 4       | pos.inv.item_variation_id               |     1 |   100.00 | Using where                        |
|  1 | PRIMARY            | phppos_location_item_variations | NULL       | eq_ref | PRIMARY,phppos_item_attribute_location_values_ibfk_2                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | PRIMARY                 | 8       | pos.phppos_item_variations.id,const     |     1 |   100.00 | NULL                               |
|  1 | PRIMARY            | phppos_suppliers                | NULL       | ref    | person_id                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | person_id               | 4       | pos.phppos_items.supplier_id            |     1 |   100.00 | NULL                               |
|  1 | PRIMARY            | phppos_categories               | NULL       | eq_ref | PRIMARY                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | PRIMARY                 | 4       | pos.phppos_items.category_id            |     1 |   100.00 | NULL                               |
|  2 | DEPENDENT SUBQUERY | inv1                            | NULL       | ref    | phppos_inventory_ibfk_1,location_id,trans_date,phppos_inventory_ibfk_4,phppos_inventory_custom                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | phppos_inventory_custom | 8       | pos.inv.trans_items,pos.inv.location_id |     3 |    50.00 | Using where; Using index           |
+----+--------------------+---------------------------------+------------+--------+---------------------------------------------------------------------------------------------------------

Explain maria db:

+------+---------------------------------------------+-------+--------------------------+
| id   | select_type        | table                           | type   | possible_keys                                                                                  | key                     | key_len | ref                                                           | rows  | Extra                    |
+------+--------------------+---------------------------------+--------+------------------------------+
|    1 | PRIMARY            | phppos_items                    | ref    | PRIMARY,deleted,deleted_system_item                                                            | deleted                 | 4       | const                                                         | 23955 | Using where              |
|    1 | PRIMARY            | inv                             | ref    | phppos_inventory_ibfk_1,location_id,phppos_inventory_custom                                    | phppos_inventory_ibfk_1 | 4       | freelance_pos5.phppos_items.item_id                           |     2 | Using where              |
|    1 | PRIMARY            | phppos_location_items           | eq_ref | PRIMARY,phppos_location_items_ibfk_2                                                           | PRIMARY                 | 8       | const,freelance_pos5.phppos_items.item_id                     |     1 |                          |
|    1 | PRIMARY            | phppos_item_variations          | eq_ref | PRIMARY,phppos_item_variations_ibfk_1                                                          | PRIMARY                 | 4       | freelance_pos5.inv.item_variation_id                          |     1 | Using where              |
|    1 | PRIMARY            | phppos_location_item_variations | eq_ref | PRIMARY,phppos_item_attribute_location_values_ibfk_2                                           | PRIMARY                 | 8       | freelance_pos5.phppos_item_variations.id,const                |     1 | Using where              |
|    1 | PRIMARY            | phppos_suppliers                | ref    | person_id                                                                                      | person_id               | 4       | freelance_pos5.phppos_items.supplier_id                       |     1 | Using where              |
|    1 | PRIMARY            | phppos_categories               | eq_ref | PRIMARY                                                                                        | PRIMARY                 | 4       | freelance_pos5.phppos_items.category_id                       |     1 | Using where              |
|    2 | DEPENDENT SUBQUERY | inv1                            | ref    | phppos_inventory_ibfk_1,location_id,trans_date,phppos_inventory_ibfk_4,phppos_inventory_custom | phppos_inventory_custom | 8       | freelance_pos5.inv.trans_items,freelance_pos5.inv.location_id |     2 | Using where; Using index |
+------+--------------------+---------------------------------+--------+------------------------------------------------------------------------------------------------+-------------------------+---------+---------------------------------------------------------------+-------+--------------------------+

Tables described (Reached StackOverflow char limit)

https://pastebin.com/nhngSHb8

Create tables:

https://pastebin.com/aWMeriqt

MYSQL (DEV BOX)

mysql> SHOW GLOBAL STATUS LIKE '%thread%';
+------------------------------------------+-------+
| Variable_name                            | Value |
+------------------------------------------+-------+
| Delayed_insert_threads                   | 0     |
| Performance_schema_thread_classes_lost   | 0     |
| Performance_schema_thread_instances_lost | 0     |
| Slow_launch_threads                      | 0     |
| Threads_cached                           | 4     |
| Threads_connected                        | 1     |
| Threads_created                          | 5     |
| Threads_running                          | 1     |
+------------------------------------------+-------+
8 rows in set (0.06 sec)

MARIA DB

MariaDB [freelance_pos5]> SHOW GLOBAL STATUS LIKE '%thread%';
+------------------------------------------+-------+
| Variable_name                            | Value |
+------------------------------------------+-------+
| Delayed_insert_threads                   | 0     |
| Performance_schema_thread_classes_lost   | 0     |
| Performance_schema_thread_instances_lost | 0     |
| Slow_launch_threads                      | 0     |
| Threadpool_idle_threads                  | 0     |
| Threadpool_threads                       | 0     |
| Threads_cached                           | 3     |
| Threads_connected                        | 2     |
| Threads_created                          | 5     |
| Threads_running                          | 1     |
| wsrep_applier_thread_count               | 0     |
| wsrep_rollbacker_thread_count            | 0     |
| wsrep_thread_count                       | 0     |
+------------------------------------------+-------+
13 rows in set (0.00 sec)

Upvotes: 0

Views: 163

Answers (3)

Milan
Milan

Reputation: 667

Moving the

WHERE inv.trans_id = (SELECT Max(inv1.trans_id) 

into the INNER JOIN is the game changer.

INNER JOIN (
    SELECT inv1.trans_items, inv1.item_variation_id, inv1.location_id, MAX(inv1.trans_id) as trans_id
        FROM phppos_inventory inv1
        WHERE inv1.trans_date < '2019-12-31 23:59:59'
    GROUP BY inv1.trans_items, inv1.item_variation_id, inv1.location_id
    ORDER BY inv1.trans_items, inv1.item_variation_id, inv1.location_id
) inv1 on inv1.trans_id = inv.trans_id
    AND inv1.trans_items = inv.trans_items 
    AND (inv1.item_variation_id = phppos_item_variations.id OR phppos_item_variations.id IS NULL) 
    AND inv1.location_id = inv.location_id

The execution is reduced from 80+s down to ~ <0.4s, on MySQL 8.0.

Upvotes: 1

The Impaler
The Impaler

Reputation: 48750

Aside with the fact that the query is misleading since the outer join is discarded, the main difference is that the second engine operation in MariabDB is an index range scan (ref) using the phppos_inventory_custom index. MySQL also chose an index range scan but over phppos_inventory_ibfk_1.

However, without the definition of these two indexes it's difficult to asses why the engines may have chosen a different path.

Please add to your question the definition of these indexes, and alse their selectivity (percent of estimated rows selected / total table rows) to elaborate more.

Upvotes: 0

Rick James
Rick James

Reputation: 142198

MariaDB's and MySQL's Optimizers started diverging significantly at 5.6. Certain queries will run queries faster in one than the other.

I think I see a way to speed up the query, perhaps on both versions.

  • Don't use LEFT JOIN when it is the same as JOIN, which seems to be the case for at least phppos_items, which has items in the WHERE that override LEFT.

  • Please provide SHOW CREATE TABLE; meanwhile, I will guess that what indexes you have/don't have, and that each table has PRIMARY KEY(id)

  • Use composite indexes where appropriate. (More below.)

  • Get the 20 rows before JOINing to the rest of the tables:

    SELECT ...
        FROM ( SELECT inv.id, pi.id
                FROM  `phppos_inventory` AS inv `inv`
                JOIN  `phppos_items` AS pi
                     ON pi.`item_id` = `inv`.`trans_items`
                  AND  inv.location_id IN( 1 )
                  AND  pi.`system_item` = 0
                  AND  pi.`deleted` = 0
                  AND  `is_service` != 1   -- Which table is this in???
                GROUP BY  pi.`item_id`
                LIMIT  20 )
        LEFT JOIN .... (( all the other tables ))
        -- no GROUP BY or LIMIT needed (I think)
    
    phppos_items:  INDEX(item_id, deleted, system_item, is_service)
    phppos_items:  INDEX(deleted, system_item, is_service)
    phppos_inventory:  INDEX(trans_items, location_id, location_id, item_variation_id, trans_date, trans_id)
    phppos_inventory:  INDEX(location_id)
    

Upvotes: 1

Related Questions