【问题标题】:How to investigate and optimise a slow MySQL query?如何调查和优化缓慢的 MySQL 查询?
【发布时间】:2018-01-25 21:27:17
【问题描述】:

我有一个在 MySQL 上运行的 drupal 7 站点。网站上的某些页面加载速度极慢。

我调查了加载时间并确定了罪魁祸首查询,该查询通常需要 10 秒才能在某些页面上执行。在一种情况下,它甚至需要 70 多秒!

查询来自“视图”,该“视图”根据网站分类从网站其他地方生成相关内容的简短列表。

这是一个慢速页面的示例(带有参数):

SELECT node.nid AS nid, node.title AS node_title, node.created AS node_created, 'podcasts:panel_pane_3' AS view_name, RAND() AS random_field 
FROM node node 
LEFT JOIN (SELECT td.*, tn.nid AS nid 
    FROM taxonomy_term_data td 
    LEFT JOIN taxonomy_vocabulary tv ON td.vid = tv.vid 
    LEFT JOIN taxonomy_index tn ON tn.tid = td.tid 
    WHERE (tv.machine_name IN ('listen')) ) taxonomy_term_data_node 
ON node.nid = taxonomy_term_data_node.nid 
LEFT JOIN taxonomy_index taxonomy_index ON node.nid = taxonomy_index.nid 
WHERE (( (taxonomy_index.tid IN ('472', '350', '742', '681', '3907', '1541', '411', '636', '990', '7757', '680', '743', '11479', '8106', '566', '2230', '11480', '766')) 
  AND (node.nid != '191314' OR node.nid IS NULL) ) 
  AND(( (node.status = '1') 
  AND (node.type IN ('article', 'experiment', 'interview', 'podcast', 'question')) ))) 
ORDER BY random_field ASC, node_created DESC 
LIMIT 5 OFFSET 0

从最初的研究来看,我认为这是添加索引的情况,但相关表的列似乎已经存在索引条目。

因此,我不确定如何进行,如果有人可以帮助我,我真的很重视一些指导吗?

PS - 我确实要求 MySQL 解释自己,这是生成的:

【问题讨论】:

  • 谷歌explain statement
  • 只需在语句前加上“EXPLAIN ...”。 MySQL 不会执行查询,而是打印一个关于查询执行计划各个方面的信息表。要了解这些,您需要阅读一些documentation
  • 谢谢你们;是的,我已经这样做了,对于没有包括现在添加到上面的输出,我深表歉意。
  • 优化 SQL 查询是一门艺术 :) 要有耐心,愿意大量阅读和工作。
  • 谢谢;我准备两者都做,但我不知道从哪里开始......

标签: mysql performance optimization


【解决方案1】:

一些优化此查询的建议:

  1. 避免选择不必要的列:您真的需要td.* 中的所有列吗?在大多数情况下,这意味着有太多信息通过网络传递给应用程序。
  2. 混合 ORDER BY 方向:您按两列排序:random_field ASC, node_created DESC。按不同的顺序排序会阻止索引的使用,这会减慢搜索速度。您认为同时制作 ASC 或同时制作 DESC 有意义吗?
  3. 我假设taxonomy_index.tid 是数字,'node.nid' 和'node.status' 也是如此。在这种情况下,当将它们与常量进行比较时,不要在常量周围添加引号,因为它会导致不需要的强制转换,这可能会阻止索引的使用。例如,将node.status = '1' 转为node.status = 1
  4. 您将加入一个子查询 (taxonomy_term_data_node) - 如果您使用的是 MySQL

因此,要应用大多数更改(不需要您决定的更改,例如上面的第 1 部分和第 2 部分),请执行以下步骤:

首先,通过添加这些索引来索引主查询:

ALTER TABLE
  `node`
ADD
  INDEX `node_idx_status_nid_title_created` (`status`, `nid`, `title`, `created`);

ALTER TABLE
  `taxonomy_index`
ADD
  INDEX `taxonomy_index_idx_nid` (`nid`);

ALTER TABLE
  `taxonomy_index`
ADD
  INDEX `taxonomy_index_idx_tid_nid` (`tid`, `nid`);

ALTER TABLE
  `taxonomy_term_data`
ADD
  INDEX `taxonomy_term_data_idx_vid_tid` (`vid`, `tid`);

ALTER TABLE
  `taxonomy_vocabulary`
ADD
  INDEX `taxonomy_vocabulary_idx_vid` (`vid`);

首先,创建临时表:

CREATE TEMPORARY TABLE IF NOT EXISTS temp1 AS SELECT
        taxonomy_term_data.*,
        tn.nid AS nid 
    FROM
        taxonomy_term_data td 
    LEFT JOIN
        taxonomy_vocabulary tv 
            ON td.vid = tv.vid 
    LEFT JOIN
        taxonomy_index tn 
            ON tn.tid = td.tid 
    WHERE
        (
            tv.machine_name IN (
                'listen'
            )
        );

现在索引子查询: 更改表temp1 添加索引temp1_idx_nid (nid);

外部查询将加入它:

SELECT
        node.nid AS nid,
        node.title AS node_title,
        node.created AS node_created,
        'podcasts:panel_pane_3' AS view_name,
        RAND() AS random_field 
    FROM
        node node 
    LEFT JOIN
        temp1 taxonomy_term_data_node 
            ON node.nid = taxonomy_term_data_node.nid 
    LEFT JOIN
        taxonomy_index taxonomy_index 
            ON node.nid = taxonomy_index.nid 
    WHERE
        (
            (
                (
                    taxonomy_index.tid IN (
                        '472', '350', '742', '681', '3907', '1541', '411', '636', '990', '7757', '680', '743', '11479', '8106', '566', '2230', '11480', '766'
                    )
                ) 
                AND (
                    node.nid != '191314' 
                    OR node.nid IS NULL
                )
            ) 
            AND (
                (
                    (
                        node.status = '1'
                    ) 
                    AND (
                        node.type IN (
                            'article', 'experiment', 'interview', 'podcast', 'question'
                        )
                    )
                )
            )
        ) 
    ORDER BY
        random_field ASC,
        node_created DESC LIMIT 5

【讨论】:

  • 非常感谢 - 我正在处理此案,并希望感谢您的善意贡献。我会在处理这个问题时报告。非常感谢。
  • @Chris,当然,希望这会有所帮助。如果您能对此进行报告,将不胜感激,如果它有帮助,请投票并接受答案。
  • 感谢@Tomer - 立即实施您的解决方案并非易事,因为 mysql 对象指向临时表(它引发了错误),因为我需要为表列指定变量特征;我需要查看数据是什么,这样我就知道该放什么了……
  • @Chris,我完全理解,慢慢来。期待在您实施更改后收到回复。另外,另一个想法 - 在应用程序本身的代码中实现所有内容之前,您可以尝试使用示例值的解决方案,看看它是否提高了性能。
  • 谢谢;是的,我曾考虑在实施之前使用上面的查询进行基准测试并展示改进;但也许您可以建议我 - mysql 对象,因为它希望我为临时表中的列指定数据类型。这不包括在您上面的示例中。正确的语法是什么?
【解决方案2】:

谢谢大家的指导。但是,我在剑桥大学的 Andy Batey 的帮助下解决了这个问题。

线索是比较上面的查询在 MySQL v5.5(非常快的结果)和 v5.7(非常慢的结果)上运行时生成的 EXPLAIN 语句;他们的查询在两个平台上的处理方式截然不同。

关键是将这个添加到 my.cnf:

optimizer_switch='derived_merge=off'

现在本机查询的执行时间为 50 毫秒或更短,而之前需要 12 秒或更长时间。

我希望这对遇到此升级问题的其他人有所帮助。

【讨论】:

    猜你喜欢
    • 2015-07-30
    • 1970-01-01
    • 2012-08-06
    • 1970-01-01
    • 2016-11-30
    • 1970-01-01
    • 1970-01-01
    • 2014-08-04
    • 1970-01-01
    相关资源
    最近更新 更多