【发布时间】: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