【问题标题】:MySQL group by kills the query performanceMySQL group by 会影响查询性能
【发布时间】:2016-06-16 02:21:35
【问题描述】:

我有 MySQL 查询,当前选择并加入 13 个表,最后对约 60k 行进行分组。没有分组的查询需要约 0 毫秒,但分组查询时间增加到约 1.7 秒。用于分组的字段是主字段并被索引。问题可能出在哪里?

我知道没有聚合的 group by 被认为是无效查询和不好的做法,但我需要不同的基表行并且不能使用 DISTINCT 语法。

查询本身如下所示:

SELECT `table_a`.*
FROM   `table_a` 
       LEFT JOIN `table_b` 
              ON `table_b`.`invoice` = `table_a`.`id` 
       LEFT JOIN `table_c` AS `r1` 
              ON `r1`.`invoice_1` = `table_a`.`id` 
       LEFT JOIN `table_c` AS `r2` 
              ON `r2`.`invoice_2` = `table_a`.`id` 
       LEFT JOIN `table_a` AS `i1` 
              ON `i1`.`id` = `r1`.`invoice_2` 
       LEFT JOIN `table_a` AS `i2` 
              ON `i2`.`id` = `r2`.`invoice_1` 
       JOIN `table_d` AS `_u0` 
         ON `_u0`.`id` = 1 
       LEFT JOIN `table_e` AS `_ug0` 
              ON `_ug0`.`user` = `_u0`.`id` 
       JOIN `table_f` AS `_p0` 
         ON ( `_p0`.`enabled` = 1 
              AND ( ( `_p0`.`role` < 2 
                      AND `_p0`.`who` IS NULL ) 
                     OR ( `_p0`.`role` = 2 
                          AND ( `_p0`.`who` = '0' 
                                 OR `_p0`.`who` = `_u0`.`id` ) ) 
                     OR ( `_p0`.`role` = 3 
                          AND ( `_p0`.`who` = '0' 
                                 OR `_p0`.`who` = `_ug0`.`group` ) ) ) ) 
            AND ( `_p0`.`action` = '*' 
                   OR `_p0`.`action` = 'read' ) 
            AND ( `_p0`.`related_table` = '*' 
                   OR `_p0`.`related_table` = 'table_name' ) 
       JOIN `table_a` AS `_e0` 
         ON ( ( `_p0`.`related_id` = 0 
                 OR `_p0`.`related_id` = `_e0`.`id` 
                 OR `_p0`.`related_user` = `_e0`.`user` 
                 OR `_p0`.`related_group` = `_e0`.`group` ) 
               OR ( `_p0`.`role` = 0 
                    AND `_e0`.`user` = `_u0`.`id` ) 
               OR ( `_p0`.`role` = 1 
                    AND `_e0`.`group` = `_ug0`.`group` ) ) 
            AND `_e0`.`id` = `table_a`.`id` 
       JOIN `table_d` AS `_u1` 
         ON `_u1`.`id` = 1 
       LEFT JOIN `table_e` AS `_ug1` 
              ON `_ug1`.`user` = `_u1`.`id` 
       JOIN `table_f` AS `_p1` 
         ON ( `_p1`.`enabled` = 1 
              AND ( ( `_p1`.`role` < 2 
                      AND `_p1`.`who` IS NULL ) 
                     OR ( `_p1`.`role` = 2 
                          AND ( `_p1`.`who` = '0' 
                                 OR `_p1`.`who` = `_u1`.`id` ) ) 
                     OR ( `_p1`.`role` = 3 
                          AND ( `_p1`.`who` = '0' 
                                 OR `_p1`.`who` = `_ug1`.`group` ) ) ) ) 
            AND ( `_p1`.`action` = '*' 
                   OR `_p1`.`action` = 'read' ) 
            AND ( `_p1`.`related_table` = '*' 
                   OR `_p1`.`related_table` = 'table_name' ) 
       JOIN `table_g` AS `_e1` 
         ON ( ( `_p1`.`related_id` = 0 
                 OR `_p1`.`related_id` = `_e1`.`id` 
                 OR `_p1`.`related_user` = `_e1`.`user` 
                 OR `_p1`.`related_group` = `_e1`.`group` ) 
               OR ( `_p1`.`role` = 0 
                    AND `_e1`.`user` = `_u1`.`id` ) 
               OR ( `_p1`.`role` = 1 
                    AND `_e1`.`group` = `_ug1`.`group` ) ) 
            AND `_e1`.`id` = `table_a`.`company` 
WHERE  `table_a`.`date_deleted` IS NULL 
       AND `table_a`.`company` = 4
       AND `table_a`.`type` = 1
       AND `table_a`.`date_composed` >= '2016-05-04 14:43:55' 
GROUP BY `table_a`.`id`

【问题讨论】:

  • 基于该查询、大小调整和十几个表,我会说 1.7 秒相当不错
  • 您还可以对查询进行解释,以了解实际的“瓶颈”在哪里。
  • @jeff 我确实这样做了,但我以前没有使用它的经验,所以很难采取行动,我在这里复制了它:jsfiddle.net/x1q02108,有什么建议吗?
  • @Drew 对我来说不够快 - 有什么建议可以让它更快吗?尝试了子查询,但收益微不足道
  • 说起来容易,但要重新设计你的逻辑和架构。知道你有一个痛苦的数据库引擎来找出与索引无关并解释

标签: mysql innodb


【解决方案1】:

ORs 杀戮性能。

此复合索引可能会有所帮助:INDEX(company, type, date_deleted, date_composed)

LEFT JOIN table_b ON table_b.invoice = table_a.id 似乎除了减慢处理速度之外什么也没做。未使用 table_bSELECTed 的字段。由于它是一个LEFT 连接,它不会限制输出。等等。如果它摆脱它,或者证明它是合理的。

其他连接同上。

JOINGROUP BY 会发生什么:首先,执行所有连接;这会爆炸中间“表”中的行数。然后GROUP BY 内爆行集。

避免这种爆炸-内爆缓慢的一种技术是做

SELECT ...,
    ( SELECT ... ) AS ...,
    ...

而不是JOINLEFT JOIN。但是,只有在子查询中有 0 行或 1 行时才有效。通常,当可以将聚合(例如 SUM)移动到子查询中时,这很有用。

如需进一步讨论,请添加SHOW CREATE TABLE

【讨论】:

  • 您的意思是 ORs 与分组协作会破坏性能?因为只有选择和加入行才能快速点亮。我尝试过复合索引,但没有太大影响。如果某些表似乎没有使用,它们实际上有时是 - 我当前的 ORM 在选择实际使用的表时有一些限制。我试过用子查询,时间没有改善,反而增加了。我的子查询总是返回多行。您对哪种表结构感兴趣 - 所有 13 个表?我可以保证所有字段都有正确的索引和字段类型 - 你永远不知道。
  • x=1 OR y=2 不能使用任何类型的索引;因此过滤效率低下(WHERE)。
  • 但是如果没有分组,我怎么感觉不到效率低下?
  • "0ms" 通常意味着查询缓存已启用并且查询是最近运行的。添加标志再次运行:SELECT SQL_NO_CACHE ...
  • 好主意,但是当我添加 SQL_NO_CACHE 时,我仍然可以接受 15 - 30 毫秒
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-19
  • 2021-04-18
  • 2014-06-21
  • 2022-06-14
  • 2011-08-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多