当解释看起来不错时如何提高查询时间?

Posted

技术标签:

【中文标题】当解释看起来不错时如何提高查询时间?【英文标题】:How to improve query time when explain looks good? 【发布时间】:2018-03-13 08:43:50 【问题描述】:

我有 12 558 392 条记录的大表“放置”;

当我尝试使用此表获取数据时,我遇到了性能问题(加载时间约为 5 秒)。 当我解释这个查询时,一切看起来都很好,但查询时间太长了。

我的查询示例:

    SELECT SQL_NO_CACHE om.*
    FROM order_materials om
          INNER JOIN material m ON om.material_id = m.id AND om.deleted = FALSE
          INNER JOIN placement p ON m.id = p.material_id AND m.deleted = FALSE AND p.deleted = FALSE
          INNER JOIN block b ON p.block_id = b.id AND b.deleted = FALSE
          INNER JOIN orders o ON om.order_id = o.id AND o.deleted = FALSE
          INNER JOIN product pr ON pr.mediaPlan_id = p.mediaplan_id
    WHERE
           b.advTable_id = 139
           AND p.date >= '2018-03-01 00:00:00' AND p.date <= '2018-04-01 00:00:00'
    GROUP BY om.material_id;

解释:

    +----+-------------+-------+------------+--------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------+---------+---------------------------+------+----------+----------------------------------------------+
    | id | select_type | table | partitions | type   | possible_keys                                                                                                                                                                                                             | key                  | key_len | ref                       | rows | filtered | Extra                                        |
    +----+-------------+-------+------------+--------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------+---------+---------------------------+------+----------+----------------------------------------------+
    |  1 | SIMPLE      | b     | NULL       | ref    | PRIMARY,FK597C48D47B04A3                                                                                                                                                                                                  | FK597C48D47B04A3     | 5       | const                     |  455 |    50.00 | Using where; Using temporary; Using filesort |
    |  1 | SIMPLE      | p     | NULL       | ref    | FK6ADE12E521DC3251,FK6ADE12E59B1CA2F1,FK6ADE12E5AFA9B543,date_ind,placement_b,placement_material_id_mediaplan_id_index,placement_material_id,placement_material_id_mediaplan_id_order_id_block_id_index,block_id_date_ind | FK6ADE12E521DC3251   | 5       | openmarket.b.id           |  135 |     0.82 | Using where                                  |
    |  1 | SIMPLE      | pr    | NULL       | ref    | FKED8DCCEF9B1CA2F1                                                                                                                                                                                                        | FKED8DCCEF9B1CA2F1   | 5       | openmarket.p.mediaplan_id |    1 |   100.00 | Using index                                  |
    |  1 | SIMPLE      | m     | NULL       | eq_ref | PRIMARY                                                                                                                                                                                                                   | PRIMARY              | 4       | openmarket.p.material_id  |    1 |    50.00 | Using where                                  |
    |  1 | SIMPLE      | om    | NULL       | ref    | FK_order_materials_1,FK_order_materials_2                                                                                                                                                                                 | FK_order_materials_2 | 4       | openmarket.p.material_id  |    2 |    50.00 | Using where                                  |
    |  1 | SIMPLE      | o     | NULL       | eq_ref | PRIMARY                                                                                                                                                                                                                   | PRIMARY              | 4       | openmarket.om.order_id    |    1 |    50.00 | Using where                                  |
    +----+-------------+-------+------------+--------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------+---------+---------------------------+------+----------+----------------------------------------------+

显示创建表order_materials

    CREATE TABLE `order_materials` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `order_id` int(11) NOT NULL,
      `material_id` int(11) NOT NULL,
      `deleted` bit(1) NOT NULL DEFAULT b'0',
      `created_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
      `updated_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
      `last_mod_user_id` int(11) DEFAULT NULL,
      `placements_count` int(11) NOT NULL DEFAULT '0',
      PRIMARY KEY (`id`),
      KEY `FK_order_materials_1` (`order_id`),
      KEY `FK_order_materials_2` (`material_id`),
      CONSTRAINT `FK_order_materials_1` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`),
      CONSTRAINT `FK_order_materials_2` FOREIGN KEY (`material_id`) REFERENCES `material` (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=251369 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

显示创建表展示位置

    CREATE TABLE `placement` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `changeDate` datetime DEFAULT NULL,
      `date` datetime NOT NULL,
      `plannedPosition` tinyint(4) DEFAULT NULL,
      `realPosition` tinyint(4) DEFAULT NULL,
      `positionWithPolitics` tinyint(4) DEFAULT NULL,
      `material_id` int(11) DEFAULT NULL,
      `mediaplan_id` int(11) NOT NULL,
      `block_id` int(11) DEFAULT NULL,
      `visible` bit(1) NOT NULL,
      `blockStartTime` datetime DEFAULT NULL,
      `price` float DEFAULT NULL,
      `pricedPrice` float DEFAULT NULL,
      `actualStartTime` datetime DEFAULT NULL,
      `playedPosition` tinyint(4) DEFAULT NULL,
      `status` int(11) DEFAULT NULL,
      `played_material_id` int(11) DEFAULT NULL,
      `deleted` bit(1) NOT NULL DEFAULT b'0',
      `created_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
      `updated_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
      `last_mod_user_id` int(11) DEFAULT NULL,
      `conflict_status_lid` int(11) DEFAULT NULL COMMENT 'lookup category placement_conflict',
      `conflict_by_type_in_block` bit(1) NOT NULL DEFAULT b'0',
      `conflict_by_type_near` bit(1) NOT NULL DEFAULT b'0',
      `conflict_by_time_overflow` bit(1) NOT NULL DEFAULT b'0',
      `conflict_by_position` bit(1) NOT NULL DEFAULT b'0',
      `order_id` int(11) DEFAULT NULL,
      `order_status_lid` int(11) DEFAULT NULL,
      `play_type_lid` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `FK6ADE12E521DC3251` (`block_id`),
      KEY `FK6ADE12E59B1CA2F1` (`mediaplan_id`),
      KEY `FK6ADE12E5AFA9B543` (`material_id`),
      KEY `blockstarttime` (`blockStartTime`),
      KEY `date_ind` (`date`),
      KEY `bst_rp_ind` (`blockStartTime`,`realPosition`),
      KEY `status_ind` (`status`),
      KEY `FK_played_material` (`played_material_id`),
      KEY `FK_placement_1` (`conflict_status_lid`),
      KEY `FK_placement_2` (`order_id`),
      KEY `FK_placement_3` (`order_status_lid`),
      KEY `FK_placement_4` (`play_type_lid`),
      KEY `placement_b` (`date`,`blockStartTime`,`block_id`,`plannedPosition`),
      KEY `placement_material_id_mediaplan_id_index` (`material_id`,`mediaplan_id`),
      KEY `placement_material_id` (`material_id`,`mediaplan_id`,`order_id`),
      KEY `placement_material_id_mediaplan_id_order_id_block_id_index` (`material_id`,`mediaplan_id`,`order_id`,`block_id`),
      KEY `block_id_date_ind` (`block_id`,`date`),
      CONSTRAINT `FK6ADE12E521DC3251` FOREIGN KEY (`block_id`) REFERENCES `block` (`id`),
      CONSTRAINT `FK6ADE12E59B1CA2F1` FOREIGN KEY (`mediaplan_id`) REFERENCES `mediaplan` (`id`),
      CONSTRAINT `FK6ADE12E5AFA9B543` FOREIGN KEY (`material_id`) REFERENCES `material` (`id`),
      CONSTRAINT `FK_placement_1` FOREIGN KEY (`conflict_status_lid`) REFERENCES `lookups` (`id`),
      CONSTRAINT `FK_placement_2` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`),
      CONSTRAINT `FK_placement_3` FOREIGN KEY (`order_status_lid`) REFERENCES `lookups` (`id`),
      CONSTRAINT `FK_placement_4` FOREIGN KEY (`play_type_lid`) REFERENCES `lookups` (`id`),
      CONSTRAINT `FK_played_material` FOREIGN KEY (`played_material_id`) REFERENCES `played_material` (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=12578822 DEFAULT CHARSET=utf8 COMMENT='Розміщення рекламного матеріалу')
            ) ENGINE=InnoDB AUTO_INCREMENT=251369 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci        

exist 重写查询后,我有下一个解释

    +----+--------------------+-------+------------+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------+---------+---------------------------+--------+----------+-------------+
    | id | select_type        | table | partitions | type   | possible_keys                                                                                                                                                     | key                   | key_len | ref                       | rows   | filtered | Extra       |
    +----+--------------------+-------+------------+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------+---------+---------------------------+--------+----------+-------------+
    |  1 | PRIMARY            | om    | NULL       | ALL    | NULL                                                                                                                                                              | NULL                  | NULL    | NULL                      | 243300 |    50.00 | Using where |
    |  6 | DEPENDENT SUBQUERY | o     | NULL       | eq_ref | PRIMARY                                                                                                                                                           | PRIMARY               | 4       | openmarket.om.order_id    |      1 |    50.00 | Using where |
    |  2 | DEPENDENT SUBQUERY | m     | NULL       | eq_ref | PRIMARY                                                                                                                                                           | PRIMARY               | 4       | openmarket.om.material_id |      1 |   100.00 | Using where |
    |  3 | DEPENDENT SUBQUERY | p     | NULL       | ref    | FK6ADE12E5AFA9B543,date_ind,placement_b,placement_material_id_mediaplan_id_index,placement_material_id,placement_material_id_mediaplan_id_order_id_block_id_index | placement_material_id | 5       | openmarket.m.id           |    101 |     0.82 | Using where |
    |  5 | DEPENDENT SUBQUERY | pr    | NULL       | ref    | FKED8DCCEF9B1CA2F1                                                                                                                                                | FKED8DCCEF9B1CA2F1    | 5       | openmarket.p.mediaplan_id |      1 |   100.00 | Using index |
    |  4 | DEPENDENT SUBQUERY | b     | NULL       | eq_ref | PRIMARY,FK597C48D47B04A3                                                                                                                                          | PRIMARY               | 4       | openmarket.p.block_id     |      1 |     5.00 | Using where |
    +----+--------------------+-------+------------+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------+---------+---------------------------+--------+----------+-------------+

【问题讨论】:

你想要左连接吗?如果是这样,请将 WHERE 子句条件移至 ON 子句。 (现在你得到常规的 INNER JOIN 结果。) 在没有任何聚合函数的情况下,group by 子句是没有根据的 @jarlh 如果内部联接实际上不是我们想要的,那么可以完全删除联接 @Strawberry,真的! 如果你已经有group by,为什么还要使用distinct?值已经是不同的(因为它们将具有不同的material_id)。尝试删除它(它可能会从解释中删除文件排序)并将左连接替换为内部(因为您已经通过 where 条件将它们设为内部) 【参考方案1】:

据我从您的架构中了解到,您正在寻找一个不同的“order_materials”列表,以根据其他列出的表格进行过滤。 根据我的经验,您无法使用连接过滤/分组/不同的结果,而不会遭受“使用临时 - 使用文件排序”

无论如何,在您的情况下,由于看起来您不需要其他表中的值,我相信可以重写您的查询,删除所有连接并仅使用 EXISTS 子句。

我会尝试这样的:

SELECT SQL_NO_CACHE om.*
FROM order_materials om 
where 
om.deleted = false
and 
exists (
    select 1 
    from material m
    where exists 
        (select 1 from placement p 
        where m.id = p.material_id 
        AND m.deleted = FALSE 
        AND p.deleted = FALSE
        and p.date >= '2018-03-01 00:00:00' AND p.date <= '2018-04-01 00:00:00'
        and exists (select 1 from block b
            where p.block_id = b.id 
            AND b.deleted = FALSE
            and b.advTable_id = 139
            )
        and exists (select 1 from product pr 
            where pr.mediaPlan_id = p.mediaplan_id)
        )
    and m.id=om.material_id
)
and exists (select 1 from orders o 
    where om.order_id = o.id 
    AND o.deleted = FALSE)

这样一个查询的解释计划是:

+------+--------------------+-------------+--------+-----------------------------------+--------------+---------+--------------------+------+-------------+
| id   | select_type        | table       | type   | possible_keys                     | key          | key_len | ref                | rows | Extra       |
+------+--------------------+-------------+--------+-----------------------------------+--------------+---------+--------------------+------+-------------+
|    1 | PRIMARY            | om          | ALL    | order_id,material_id              | NULL         | NULL    | NULL               |    1 | Using where |
|    1 | PRIMARY            | m           | eq_ref | PRIMARY                           | PRIMARY      | 4       | abc.om.material_id |    1 | Using where |
|    1 | PRIMARY            | o           | eq_ref | PRIMARY                           | PRIMARY      | 4       | abc.om.order_id    |    1 | Using where |
|    3 | DEPENDENT SUBQUERY | p           | ALL    | material_id,block_id,mediaplan_id | NULL         | NULL    | NULL               |    1 | Using where |
|    3 | DEPENDENT SUBQUERY | <subquery5> | eq_ref | distinct_key                      | distinct_key | 4       | func               |    1 |             |
|    3 | DEPENDENT SUBQUERY | b           | eq_ref | PRIMARY,advTable_id               | PRIMARY      | 4       | abc.p.block_id     |    1 | Using where |
|    5 | MATERIALIZED       | pr          | index  | mediaPlan_id                      | mediaPlan_id | 5       | NULL               |    1 | Using index |
+------+--------------------+-------------+--------+-----------------------------------+--------------+---------+--------------------+------+-------------+

如您所见,没有临时文件,没有文件排序。因此,由于存在是一个布尔运算符,因此您不会像加入时那样收到重复。

最后,请格外小心:我是即时写下这个答案的,您需要检查嵌套的存在是否与您的预期结果一致,这不是最终解决方案,而只是对我的共享提示学到为止。

【讨论】:

我已经尝试过这个解决方案,但它的工作速度很慢4m 52s 307ms 你能发布你的解释计划吗?当我尝试我的建议时,我使用了一个非常简单的架构 我在问题描述中添加了解释 我的错误:我提出的查询返回 order_material 表的所有行。显然,您只需要 b.advTable_id = 139p.date &gt;= '2018-03-01 00:00:00' AND p.date &lt;= '2018-04-01 00:00:00' 的那些,正如我告诉您的,这只是一个提示,您需要改进查询。在这种情况下,可能需要一些连接。 另一个建议,给定 om 表的行,是更改“where”的顺序,以便您将 om 表作为内部表,并从其他表中获取值【参考方案2】:

order_materials 似乎是一个多:多映射表。架构效率低下。摆脱id 并进行here 中建议的其他更改。

block 需要INDEX(advTable_id, deleted, id)

placement 需要INDEX(material_id, deleted, date)

有几个冗余索引。遵循这条规则:如果你有INDEX(a, b),你就不需要INDEX(a)

key_len = 5 通常指的是INT NULL -- 检查它们是否应该是INT NOT NULL

【讨论】:

以上是关于当解释看起来不错时如何提高查询时间?的主要内容,如果未能解决你的问题,请参考以下文章

如何修复 Github 页面上的 HTTP 404?

如何提高包含部分公共子查询的 SQL 查询性能

如何做SQL优化?优化SQL语句,提高SQL查询效率。Effective MySQL之SQL语句最优化

如何简化/提高这个 MySQL 查询的性能?

当方向是横向xcode时如何使屏幕可滚动

如何提高大型表上基于日期的查询性能?