我对基本的mysql数据库优化有疑问。 我有3个表,文章,标签和标签(这是一个连接表)。
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表中我指定的标签,然后(通过索引)能够立即检索相关的属性,它应该都非常很快,显然我的想法出了问题。
由于
[编辑] - 杰伊的评论
我添加了10k文章,30k标签和6个标签,还在tag.name和taggings.tag_id上添加了2个索引,查询仍需要很长时间才能运行,0.5-1秒,EXPLAIN在下面。 alt text http://dl.dropbox.com/u/2306276/EXPLAIN%20results%203.png
答案 0 :(得分:2)
因为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
答案 1 :(得分:1)
您也可以尝试使用两次加入表而不是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"
答案 2 :(得分:1)
这里有几件事情。
首先,你的桌子目前显然非常小。当表很小时,DBMS通常会更快地读取整个事物而不是使用任何索引。要获得有意义的EXPLAIN结果,您需要在表格中获得实际数量的记录。
看起来您还将“id”字段声明为主键。主键是索引的子类,因此它们应该可用。请注意,解释计划表明它使用主键来查找标记记录。
此查询的明显起点是标签。因此,如果这是一个重要的查询,我将创建一个索引标签(名称)。然后它不需要按顺序搜索Tags表。
从那里它应该通过tag_id查找Taggings。所以你应该有一个索引。
然后它可以通过article_id查找文章。这是主键,因此应该已经存在。
所以我认为你会得到两个索引最有效的计划:标签(名称)和标签(tag_id)。