【问题标题】:Brutally slow MySQL Query极其缓慢的 MySQL 查询
【发布时间】:2011-11-18 08:33:00
【问题描述】:

我很乐意就此提出任何建议 - 无论是重写查询,还是以不同方式设置表。

我拥有的基本上是三个表——一个产品表、一个位置表和一个条件表。位置表存储有关整个时间位置的所有信息,与条件相同。这个海量查询的诀窍是只用它们的最新条件和位置来挑选产品。

我从这个问题中得到了大致的想法:MySQL MIN/MAX returning proper value, but not the related record info

答案是否只是将当前位置和条件存储在主产品表中,并保留这些历史表,但不使用它们进行搜索?我喜欢将它们分开的想法,但是这个查询当然需要 50 秒才能运行,这根本不实用。

SELECT 
'$table' AS tablename, 
$table.id, 
product_name, 
$table.status,
CL.event AS last_event,
CONCAT_WS(' ', CL.location, CL.floor, CL.bin, CL.bay) AS current_loc,
CC.status AS current_cond
FROM $table

LEFT OUTER JOIN
    (SELECT DISTINCT
        C.work_type,
        C.work_id,
        C.status,
        C.inspected_timestamp
        FROM
        (SELECT 
            CONCAT(work_type, work_id) AS condition_id, 
            status,
            MAX(inspected_timestamp) as current
            FROM conditions
            GROUP BY condition_id
        ) XC
    JOIN conditions C
      on CONCAT(C.work_type, C.work_id) = XC.condition_id
      and C.inspected_timestamp = XC.current
    ) CC ON 
    $table.id = CC.work_id AND 
    CC.work_type = '$table'                         

LEFT OUTER JOIN
    (SELECT DISTINCT
        L.work_type,
        L.work_id,
        L.event,
        L.location,
        L.floor,
        L.bin,
        L.bay,
        L.timestamp
        FROM
        (SELECT
            CONCAT(work_type, work_id) AS location_id, 
            location,
            MAX(timestamp) as current
            FROM locations
            GROUP BY location_id
        ) XL
    JOIN locations L
      on CONCAT(L.work_type, L.work_id) = XL.location_id
      and L.timestamp = XL.current
    ) CL ON 
    $table.id = CL.work_id AND 
    CL.work_type = '$table'

HAVING last_event = 'Received'

我在这里添加了 EXTENDED EXPLAIN 的结果。

[0] => Array ( 
    [id] => 1 
    [select_type] => PRIMARY 
    [table] => paintings 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 1159 
    [filtered] => 100.00 
    [Extra] => )

[1] => Array ( 
    [id] => 1 
    [select_type] => PRIMARY 
    [table] => 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 3211 
    [filtered] => 100.00 
    [Extra] => ) 

[2] => Array ( 
    [id] => 1 
    [select_type] => PRIMARY 
    [table] => 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 1870 
    [filtered] => 100.00 
    [Extra] => ) 

[3] => Array ( 
    [id] => 4 
    [select_type] => DERIVED 
    [table] => 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 1868 
    [filtered] => 100.00 
    [Extra] => Using temporary )

[4] => Array ( 
    [id] => 4 
    [select_type] => DERIVED 
    [table] => L 
    [type] => ref 
    [possible_keys] => timestamp 
    [key] => timestamp 
    [key_len] => 8 
    [ref] => XL.current 
    [rows] => 5 
    [filtered] => 100.00 
    [Extra] => Using where ) 

[5] => Array ( 
    [id] => 5 
    [select_type] => DERIVED 
    [table] => locations 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 3913 
    [filtered] => 100.00 
    [Extra] => Using temporary; Using filesort ) 

[6] => Array ( 
    [id] => 2 
    [select_type] => DERIVED 
    [table] => 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 3191 
    [filtered] => 100.00 
    [Extra] => Using temporary ) 

[7] => Array ( 
    [id] => 2 
    [select_type] => DERIVED 
    [table] => C 
    [type] => ref 
    [possible_keys] => inspected_timestamp 
    [key] => inspected_timestamp 
    [key_len] => 8 
    [ref] => XC.current 
    [rows] => 45 
    [filtered] => 100.00 
    [Extra] => Using where ) 

[8] => Array (
     [id] => 3 
    [select_type] => DERIVED 
    [table] => conditions 
    [type] => index 
    [possible_keys] => 
    [key] => work_type_2 
    [key_len] => 316 
    [ref] => 
    [rows] => 3986 
    [filtered] => 100.00 
    [Extra] => Using index; Using temporary; Using filesort )

【问题讨论】:

  • 您应该做的第一件事是发出 EXPLAIN EXTENDED 查询...并包含此信息。 SHOW CREATE TABLE ... 所涉及的表也不会受到伤害。
  • 我添加了扩展解释结果。谢谢你的建议。我看到很多临时表,也许创建这些是问题所在。
  • 它是EXPLAIN EXTENDED

标签: mysql sql join query-optimization


【解决方案1】:

你可以做一些事情:

  1. 在查询中解释计划。看看那里是否有 TABLE SCAN。这就是凶手。
  2. 查看重新排列查询是否会对 EXPLAIN PLAN 结果产生影响。尽早过滤掉更多记录将减少所需时间。
  3. 检查以确保每个 WHERE 子句中的列都有索引。
  4. 涉及的记录越多,查询的时间就越长。你保留了多少历史?你说的是多少行?您应该制定一项政策,删除早于保留期限的记录,并将它们放入历史记录或报告架构中。
  5. 您能否利用触发器或视图来预先计算任何这些值?

【讨论】:

  • 我添加了 EXPLAIN EXTENDED 结果。我没有看到任何 TABLE SCAN,但有很多 DERIVED 和 Using Temporary Tables。这可能会减慢速度。不过你是对的,我绝对可以让一些内部查询更小。就像我只在位置搜索“已收到”一样,我可以摆脱所有没有的。谢谢,我很感激你的洞察力......并且会更感激。
【解决方案2】:

我把这个放在答案中纯粹是因为评论长度的限制。

我看了你的查询很长一段时间,我认为这主要是它的性质,以及它的编写方式导致查询花费了这么多时间,但我没有看到任何明显的东西也错了。

在您进行分组以获取摘要行的地方,然后自行加入这些查询,虽然我不完全了解您的表或数据的设计,但这将是昂贵的,如解释所示。所以它是表扫描。你也是对的,制作临时表和排序它们的成本更高。

因此,如果在所花费的时间根本无法接受的情况下,将这些值预先汇总并在汇总表中访问会很有帮助。当您查看说明时,请注意行数,因为这应该可以让您很好地了解查询所做的事情是否合理。

此外,根据定义,最后的 having 子句也不会被优化。如果有办法将其移至 where 子句或作为其中一个联接中的条件,那么您就有机会显着改进查询计划,但考虑到摘要的成本,仍然需要一些时间。

此时我唯一可以建议的是将其分解成小块,看看是否可以优化各个组件,然后重新组装。

【讨论】:

  • TBH,这只是一个答案;)
【解决方案3】:

正如@gview 所解释的,有很多因素导致此查询残酷地变慢。除了他的回答中提到的所有内容之外,还在两个表中使用了CONCAT() 函数,结果稍后用于连接这两个派生表。

如果您只想显示表product 的行,其中只有location 中的最新相关行和condition 中的最新相关行,您可以使用类似以下的内容(这只有逻辑最新的condition,您需要另一个类似的LEFT JOIN 才能获得最新的location):

SELECT 
  t.id, 
  t.product_name, 
  t.status,
  cc.status AS current_cond
FROM 
      $table AS t
  LEFT OUTER JOIN
      ( SELECT c.*
        FROM 
              conditions AS c
          JOIN
              ( SELECT 
                  work_id, 
                  MAX(inspected_timestamp) as current_ts
                FROM conditions mc
                WHERE work_type = '$table'
                GROUP BY condition_id
              ) AS mc
            ON  mc.work_id = c.work_id
            AND mc.current_ts = c.inspected_timestamp 
        WHERE c.work_type = '$table'
      ) AS cc  
    ON cc.work_id = t.id                     

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-03
    • 1970-01-01
    • 1970-01-01
    • 2011-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多