【问题标题】:Basic optimisation with an index for mysqlmysql索引的基本优化
【发布时间】:2011-03-18 03:05:25
【问题描述】:

我有一个关于基本 mysql 数据库优化的问题。 我有 3 个表,Articles、Tags 和 Taggings(这是一个连接表)。

Articles         Taggings             Tags
id               id                   id
name             article_id           name
                 tag_id

我正在使用以下查询检索与指定标签完全匹配的文章

SELECT *, COUNT(*) AS c
FROM articles AS a
JOIN taggings AS tng ON a.id = tng.article_id
JOIN tags AS t ON t.id = tng.tag_id
WHERE t.name IN ("Red","Green")
GROUP BY a.id
HAVING c = 2

这个查询很慢,所以我做了一个EXPLAIN,得到了如下结果:

alt text http://dl.dropbox.com/u/2306276/EXPLAIN%20results.png

现在,我不太明白我在这里做什么,但我认为“type: ALL”不好,所以我想我会在 taggings 表中的 article_id 和 tag_id 中添加索引(BTREE),并且再次运行查询。 alt text http://dl.dropbox.com/u/2306276/EXPLAIN%20results%202.png 好吧,在我未受过教育的人看来,这看起来并没有更好,行数与前一个相同,并且在两种情况下类型仍然是 ALL。

那么有人可以告诉我哪里出错了吗?索引不会帮我解决这个问题吗?

我的 Tag 表会保持相对较小,所以我认为查询应该扫描 Tag 表以查找我指定的标签,然后(通过索引)能够立即检索到关联的属性,并且应该都非常很快,显然我的想法有问题。

谢谢

[编辑] - 杰伊的 cmets

我加了10k篇文章,30k个taggings,6个tags,还在tag.name和taggings.tag_id上加了2个索引,查询还是跑了很长时间,0.5-1秒,下面是EXPLAIN。 alt text http://dl.dropbox.com/u/2306276/EXPLAIN%20results%203.png

【问题讨论】:

标签: sql mysql optimization indexing


【解决方案1】:

因为 tags.name 是唯一真正减少结果集中行数的列,所以必须对它进行索引以使任何基于标签的搜索查询更快。

更新:尝试运行此查询

SELECT a.*
FROM articles AS a
JOIN taggings AS tng ON a.id = tng.article_id
JOIN tags AS t ON t.id = tng.tag_id
WHERE t.name IN ("Red","Green")
GROUP BY a.id
HAVING COUNT(DISTINCT t.id) = 2

【讨论】:

  • tags 表比较小,我按照你的建议添加了索引,对查询时间没有任何影响。
  • 会影响 EXPLAIN 的输出吗?
  • 为 tags.name 添加索引的效果以及其他一些更改,可以在我对原始帖子的编辑中使用 EXPLAIN 看到。
  • 你有一个索引覆盖标签表中的两列吗?它应该是唯一的以防止重复关系。唯一键(tng.tag_id, tng.article_id)
  • 我没有,但我现在这样做了,这似乎提高了性能。 DISTINCT 也有助于加快速度。我还改用 InnoDB,速度提高了 4 倍。有了所有这些小东西,运行查询所需的时间现在看起来更合理了。 Goodness 知道美味/flickr 如何处理他们需要处理的数据量。
【解决方案2】:

这里发生了几件事。

首先,您的桌子目前显然非常小。当表很小时,DBMS 通常会发现读取整个数据比使用任何索引更快。要获得有意义的 EXPLAIN 结果,您需要在表中获得实际数量的记录。

看起来您也将“id”字段声明为主键。主键是索引的子类,因此应该可用。请注意,解释计划表明它使用主键来查找标记记录。

这个查询的明显起点是标签。所以如果这是一个重要的查询,我会创建一个标签(名称)的索引。这样就不需要顺序搜索标签表了。

从那里它应该通过 tag_id 查找标记。所以你应该有一个索引。

然后它可以通过 article_id 查找文章。这是主键,所以它应该已经存在了。

所以我认为你会得到两个索引最有效的计划:Tags(name) 和 Taggings(tag_id)。

【讨论】:

  • 感谢您的回复 Jay,我按照您的建议做了,添加了 10k 篇文章、30k 个标签和 6 个标签。我添加了您建议的索引,但查询仍然需要 0.5-1 秒才能运行。我已经编辑了我的原始帖子以显示新的解释。
  • 嗯。它在其中两个表上执行 eq_ref,这与将要获得的结果一样好。所以剩下的问题是它决定读取表格的顺序。尽管您在 Tag.name 上有一个索引,但它决定对 Taggings 进行完整的文件搜索。这有点好奇。如果您有 6 个标签,并且您正在搜索其中的 2 个,那么 DBMS 可能认为这不足以将其缩小到有用的程度。哦,添加所有记录后,您应该运行分析以更新统计信息。这可能会有所帮助。如果没有,这可能会尽可能好。
【解决方案3】:

您也可以尝试使用两次连接表而不是 GROUP BY。这有时会产生更快的查询:

SELECT a.*
FROM articles AS a
JOIN taggings AS tng1 ON a.id = tng1.article_id
JOIN tags AS t1 ON t1.id = tng1.tag_id AND t1.name = "Red"
JOIN taggings AS tng2 ON a.id = tng2.article_id
JOIN tags AS t2 ON t2.id = tng2.tag_id AND t2.name = "Green"

【讨论】:

  • 不需要 WHERE - 将它们移到 JOIN... 但实际上,如果您需要支持所有匹配的不同数量的标签,这并不是那么友好。
  • 感谢您的回复马克,但不幸的是我需要支持不同数量的标签。
  • 我刚刚发现了这篇文章joinfu.com/presentations/tagging.pdf,它和你的建议一样,我面临的问题是如何为不同数量的标签动态创建这样的查询。
  • @Jon:您可以使用字符串连接并为要包含在查询中的每个标签添加新的连接。显然,如果有很多标签,查询会变长,因此使用此方法搜索的标签数量可能会有所限制,但对于查询中相对较少的标签应该没问题。通常优化器会找到一种有效的方式来执行连接。
  • @Jon:您可能还想看看这个类似的问题,我给出了类似的答案但更详细:stackoverflow.com/questions/3260543/…
猜你喜欢
  • 2011-09-03
  • 2016-05-18
  • 1970-01-01
  • 1970-01-01
  • 2018-12-09
  • 2014-08-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多