缓慢的 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 KEY
和 wikiscore
上删除 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 查询。我应该索引啥?的主要内容,如果未能解决你的问题,请参考以下文章
如果输入一条查询一张表的sql语句,但数据库执行缓慢,如何并采取啥样的方法对数据库进行优化?