如何调查和优化慢速MySQL查询?

时间:2018-01-25 21:27:18

标签: mysql performance optimization

我在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解释自己,这就是生成的内容: mysql explain output

2 个答案:

答案 0 :(得分:0)

优化此查询的建议很少:

  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. 如果您正在使用MySQL<
  5. 您将加入子查询(taxonomy_term_data_node)。 5.6,甚至可能是MySQL 5.7,MySQL最有可能无法正确索引该子查询。因此,我建议将该子查询提取到临时表,索引它并从外部查询加入它。请参阅下面的转换。
  6. 因此,要应用大多数更改(不需要您做出决定的更改,例如上面的第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'
                )
            );
    

    现在索引子查询:     ALTER TABLE temp1 ADD INDEX temp1_idx_nidnid);

    外部查询将加入它:

    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
    

答案 1 :(得分:0)

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

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

关键是将此添加到my.cnf:

optimizer_switch='derived_merge=off'

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

我希望这可以帮助遇到此升级问题的其他任何人。