缓慢的 MySQL 查询。我应该索引啥?

Posted

技术标签:

【中文标题】缓慢的 MySQL 查询。我应该索引啥?【英文标题】:Slow MySQL query. What should I index?缓慢的 MySQL 查询。我应该索引什么? 【发布时间】:2009-02-04 19:27:26 【问题描述】:

每次保存页面编辑时,phpWiki 都会有 5 秒的慢查询。 “mysql-slow.log”中经常出现的查询是:

INSERT INTO wikiscore 
SELECT w1.topage, COUNT(*) 
FROM wikilinks AS w1, wikilinks AS w2 
WHERE w2.topage=w1.frompage 
GROUP BY w1.topage;

目前的指标如下:

table "wikilinks" has a primary index on "frompage" and "topage" 
table "wikiscore" has a primary index on "pagename" and "score"

如何重新编写 SELECT 查询以更快地返回相同的结果?我如何更改索引以便此查询更快?我的想法是它可能被过度索引了吗?

我只对查询的 SELECT 部分的结果进行了计时,仅需要 1-2 秒。 INSERT 必须占用剩下的时间。

保存我想消除的页面时存在延迟。由于已完成大量修改,我无法选择升级到另一个 wiki 引擎(或 PHPwiki 版本)。

有什么想法吗?

编辑---

查询的 SELECT 部分的“EXPLAIN”的结果是:

SIMPLE
w2
index
PRIMARY
204
31871   
Using index; Using temporary; Using filesort

SIMPLE
w1
ref
PRIMARY
PRIMARY
102 
phpwiki.w2.topage   
14
Using index

【问题讨论】:

慢查询日志现在说:# Query_time: 4 Lock_time: 0 Rows_sent: 0 Rows_examined: 416659 在我更改索引之前(参见下面的 cmets)这个日志说:# Query_time: 5 Lock_time: 0 Rows_sent : 0 Rows_examined: 445641 【参考方案1】:

表“wikilinks”在“frompage”和“topage”上有一个主索引

WHERE w2.topage=w1.frompage

无法在上述复合索引上搜索此条件。

更改订单(在 topage, frompage 上创建索引)或在 topage 上创建附加索引。

P。 S. 问题的根源在于系统中每一个页面的排名都会随着每次编辑而更新。

这个排名系统对我来说有点奇怪:它计算的是链接的链接,而不是链接本身。

如果有 1000 个页面链接到 Moscow,并且只有 Moscow 链接到 Beket Pond,那么该池塘将获得 1000 分和 Moscow 将一无所获,尽管每个人都知道莫斯科,但对池塘一无所知。

我认为这不是你的意思。很可能它应该是这样的:

INSERT INTO
       wikiscore 
SELECT
       linked.topage, COUNT(*) AS cnt
FROM   wikilinks current, wikilinks linked
WHERE  current.frompage=@current_page
       AND linked.topage = current.topage
GROUP BY
       linked.topage
ON DUPLICATE KEY UPDATE
       score = cnt;

这将汇总从当前页面引用的所有页面的所有链接,这似乎是您想要的。

在这种情况下,您需要在 PRIMARY KEYwikiscore 上删除 score,但无论如何我认为没有必要将它放在那里。

如果你想加快排名查询,你可以创建这样的索引:

ALTER TABLE wikilinks ADD CONSTRAINT pk_wikilinkes_fromto PRIMARY KEY (frompage, topage);

CREATE INDEX ix_wikilinks_topage ON wikilinks (topage);

ALTER TABLE wikiscore ADD CONSTRAINT pk_wikiscore_pagename PRIMARY KEY (pagename);

CREATE INDEX ix_wikiscore_score ON wikiscore (score);

【讨论】:

您需要一个以toppage 作为前导列的索引。它可能允许重复,并且不必包含 fromage 或任何其他列。 他的 EXPLAIN 表示开始使用索引。在我看来,查询可能会离开“w2”并使用索引通过 frompage 查找“w1”中的行。 我将 wikilinks 上的索引更改为“topage”和“frompage”,并看到 SELECT 部分的查询时间更快。然而,关于那个的解释似乎表明正在查看更多行。我不确定这是什么意思 我确保页面已被编辑,因此没有缓存在播放,(我不认为)。 按“分数”从高到低排序怎么样,不需要对“分数”进行索引吗?【参考方案2】:

使用EXPLAIN 语句来确定查询的哪一部分花费的时间应该会很有帮助。然后,您可以决定要采取哪些措施来优化您的查询。

【讨论】:

【参考方案3】:

我在理解查询的作用时遇到了一些问题。我收集它找到从一个页面到另一个页面的链接。所以 w1.topage 是指向该页面的链接,而 w1.frompage 是从该页面到其他页面的链接。因此插入添加了页面和指向该页面的链接数。

我走上正轨了吗?

你的主要问题是这一行:

FROM wikilinks AS w1, wikilinks AS w2 

如果您假设该表有 1000 个条目,则查询引擎必须将 1000 个条目与其他条目匹配,因此它会抓取 1000×1000 行(不考虑 WHERE 或 GROUP 子句)。随着您获得越来越多的条目,查询时间呈指数增长。 (轰隆隆)

此外,您只是在编辑一个页面,因此您应该能够合理地假设指向该特定页面的链接不会更改,但来自该页面的链接可能会更改。因此,不要在每次更新时都编写 wikilinks 表,而是删除此特定页面中的链接,然后将此页面中的所有链接重新插入到其他页面。

【讨论】:

目前 wikilinks 有 31871 行。但是这个查询正在创建所有页面中每个页面有多少链接的分数。我打算看看我是否可以只查询已保存页面的链接并更新 wikiscore 表中的一行。【参考方案4】:

Quassnoi 的回答会让您加快 SELECT 的速度。如果 INSERT 再花四秒钟,那么添加索引将无济于事。如果希望省略传入链接计数为零的页面,则可以通过在 SELECT 中添加 AND COUNT(*) > 0 来减少流程中的大量数据。

您至少可以通过从 wikiscore 中删除索引来获得一些改进。您在 pagename,score 上的主键实际上没有意义(您可以存储来自同一页面的多个分数,但如果它们是 same 分数则不能?),并且可能应该只是一个主键pagename 上的键。如果还有其他索引,您也许可以摆脱它们。

如果发生这种情况时没有重新创建 wikiscore,您可能会从向其抛出 OPTIMIZE TABLE 中获得一些好处。

然而,真正令人敬畏的是,如果您更改此查询背后的整个理论,这样您就无需在每次保存页面时重建 整个 wikiscore 表,而只需更新分数保存的页面和它链接到的页面。

【讨论】:

是的,这完全有道理。我将深入研究代码,看看仅更新已保存页面的分数需要多少时间。 两个表中的任何一个都没有在 phpmyadmin 中报告开销。【参考方案5】:

这是我在 PHPWiki 源代码中修改 PHP 代码的方法

// update pagescore
//old way... 
/*     
mysql_query("DELETE FROM $WikiScoreStore", $dbi["dbc"]);
mysql_query("INSERT INTO $WikiScoreStore"
                 ." SELECT w1.topage, COUNT(*) FROM $WikiLinksStore AS w1, $WikiLinksStore AS w2"
                 ." WHERE w2.topage=w1.frompage GROUP BY w1.topage", $dbi["dbc"]);

*/

//delete this pagescore            
mysql_query("DELETE FROM $WikiScoreStore WHERE pagename='$frompage'", $dbi["dbc"]);
//insert just this pagescore
mysql_query("INSERT INTO $WikiScoreStore" 
                    ." SELECT w1.topage, COUNT(*) FROM $WikiLinksStore AS w1, $WikiLinksStore AS w2"
                ." WHERE w2.topage=w1.frompage AND w1.topage='$frompage' GROUP BY w1.topage", $dbi["dbc"]);

由于此代码更改和索引调整,我没有慢查询。谢谢你!

【讨论】:

也许你应该向 PHPWiki 的维护者指出这个解决方案,以便他们可以在项目中应用它。

以上是关于缓慢的 MySQL 查询。我应该索引啥?的主要内容,如果未能解决你的问题,请参考以下文章

非常简单的 MySQL 索引查询运行非常缓慢

如果输入一条查询一张表的sql语句,但数据库执行缓慢,如何并采取啥样的方法对数据库进行优化?

如何解决SQL Server查询速度缓慢的问题

mysql里的range分区方式和主键冲突了怎么办?innodb中没有主键会造成啥影响?

mysql表应该添加啥样的索引?

sql处理千万数据查询缓慢问题