【问题标题】:MySQL Slow join - but not always and not on all tablesMySQL 慢连接 - 但并非总是如此,也不是在所有表上
【发布时间】:2011-03-07 05:09:11
【问题描述】:

我们遇到了一个非常奇怪的 MySQL 数据库的性能问题,我们需要另一双眼睛来告诉我们是否要疯了。 我们团队中有 2 位 MySQL 认证开发人员,但他们只能说:“这是不可能的”。

无论如何,情况如下:我们有一个查询,理论上应该相当快,但实际上很慢。如果我们通过删除 1 个连接来精简查询,查询会变得非常快。如果我们删除不同的连接,它仍然很慢,尽管连接的表具有几乎相同的结构。更糟糕的是:连接有时很快,有时不是......这似乎是某种随机问题,虽然它与服务器负载无关,因为我在本地系统上也有它。

表结构如下:

Table : article Rows : 57491
Field            Type                 Null   Key     Default     Extra
arti_id          int(10) unsigned     NO     PRI                 auto_increment
prev_id          int(10) unsigned     YES    MUL                 (null)
news_id          int(10) unsigned     NO     MUL                 (null)
cate_id          int(10) unsigned     NO     MUL                 (null)
pdf_id           int(10) unsigned     YES    MUL                 (null)
imag_id          int(10) unsigned     YES    MUL                 (null)
publication_date date                 NO     MUL                 (null)
title            varchar(255)         NO     MUL                 (null)
full_text        text                 YES    (null)              (null)

Table : category Rows : 3
Field            Type                 Null   Key     Default     Extra
cate_id          int(10) unsigned     NO     PRI                 auto_increment
code             varchar(7)           NO     (null)              (null)

Table : language Rows : 4
Field            Type                 Null     Key     Default     Extra
lang_id          int(10) unsigned     NO       PRI                 auto_increment
code             varchar(2)           NO       (null)              (null)

Table : newspaper Rows : 393
Field            Type                 Null     Key     Default     Extra
news_id          int(10) unsigned     NO       PRI                 auto_increment
lang_id          int(10) unsigned     NO       MUL                 (null)
name             varchar(255)         NO       UNI                 (null)

现在奇怪的部分来了:你可以看到 046_newspaper 和 046_category 都有一个主键(幸运的是)。它们都通过外键从 a046_article 引用。当我们运行以下查询时:

SELECT SQL_NO_CACHE
    article.*
FROM
    article
        INNER JOIN
        newspaper AS `n`
        ON
        article.news_id = n.news_id
ORDER BY
    article.publication_date DESC
LIMIT
    50

我们在 0.016 秒后得到结果,这非常快。

现在,当我们用带有类别的连接替换带有报纸的连接时:

SELECT SQL_NO_CACHE
    article.*
FROM
    article
        INNER JOIN
        category AS `c`
        ON
        article.cate_id = c.cate_id
ORDER BY
    article.publication_date DESC
LIMIT
    50

查询耗时 1.02 秒。

奇怪的是,情况并非总是如此。有时,没有明显的原因,第一个查询也需要那么长的时间。

最后我们要做的是:

SELECT SQL_CALC_FOUND_ROWS
    *,
    `n`.`name` AS `news_name`,
    `c`.`cate_id`,
    `c`.`code` AS `cate_name`,
    `l`.`code` AS `lang_name`
FROM
    `article`
        INNER JOIN
        `newspaper` AS `n`
        ON
        article.news_id = n.news_id
            INNER JOIN
            `category` AS `c`
            ON
            article.cate_id = c.cate_id
                INNER JOIN
                `language` AS `l`
                ON
                n.lang_id = l.lang_id
ORDER BY
    `article`.`publication_date` DESC
LIMIT
  50

此时需要超过 12 秒。这部分是由于 *,我们可以将其替换为单个字段,但仍然需要 3 秒。

我们尝试了很多方法: - 添加索引(尽管所有需要的索引都已经存在,添加更多索引是个坏主意) - 增加排序缓冲区大小和键缓冲区 - 看了很多解释... - 一遍又一遍地阅读 MySQL 手册 - 阅读很多论坛 然而,这样的事情并没有解决这个问题。

如果有人有任何想法,请随时大声疾呼!如果您需要 SQL 脚本甚至访问数据库,那么您可以试一试,请告诉我...我们的客户经常抱怨页面速度慢...

谢谢!

【问题讨论】:

  • 查询优化器可能会出现问题。奇怪的是它是随机发生的……看看 STRAIGHT_JOIN;这使您可以避免查询优化器重新排列它们。
  • 我们遇到了同样的问题,这里不再允许加入。我们通过修改查询而不是连接来解决它,我们使用 select * from table1,table2 where table1.field=table2.field。人们会告诉你没有差异,但在性能上有差异
  • 将两个查询的 EXPLAIN 输出添加到您的问题中。
  • @Grumpy:我也很难相信这一点。你有任何测试用例可以证明这一点吗?
  • @Grumpy:我已经更改了两个查询,每个查询都有几个连接并删除了连接,EXPLAIN 给出的结果没有变化。我相信您的改进来自其他地方,我不认为这通常可以被视为优化的途径,而是可能导致更难阅读的代码。

标签: mysql join performance indexing


【解决方案1】:
  1. 始终使用 EXPLAIN(QUERY) 来分析和了解 MySQL 如何解析您的查询。
  2. 检查您的 INDEXes,MySQL 为 select 选择了错误的索引。
  3. 尝试使用 SELECT 和 INDEX 提示。 http://dev.mysql.com/doc/refman/5.1/en/index-hints.html

    SELECT * FROM table1 使用索引 (col1_index,col2_index) 其中 col1=1 AND col2=2 AND col3=3;

    SELECT * FROM table1 IGNORE INDEX (col3_index) 其中 col1=1 AND col2=2 AND col3=3;

【讨论】:

    【解决方案2】:

    我会为所有表做一个 SHOW INDEX ON 表,并检查基数列是否正确(即没有 NULL)。或者,您可以对每张桌子进行 ANALYZE 分析。由于那里有 2 位经过认证的 MySQL 开发人员,您可能已经这样做了。

    下一步是查看 EXPLAIN 并了解 MySQL 是如何优化它的。您可能需要使用 FORCE、USE 或 IGNORE 来使其优化正确。由于 MySQL 和具有缓存数据(即索引)的操作系统,速度会有所不同,但当您指定“无缓存”时,查询不会。可以发一下解释吗?

    【讨论】:

      【解决方案3】:

      我认为您可以尝试使用标量查询。

      【讨论】:

        猜你喜欢
        • 2014-03-20
        • 2015-07-01
        • 2013-10-20
        • 2019-12-07
        • 1970-01-01
        • 2019-11-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多