此查询的目的是获取其他用户在某人帖子上发表的最新评论。它应该包含评论的用户的名称,他们评论的帖子的标题以及实际的评论文本。
有三个表,MySQL myisam:
comment: id, author_fk, post_fk, text, date, ...
post: id, author_fk, content, date, ...
user: id, name, ...
这就是我收到用户帖子上最新评论的方式:
SELECT comment.text, user.name, post.title
FROM comments
JOIN user ON user.id = comment.author_fk
JOIN post ON post.id = comment.post_fk
WHERE post.author_fk = [id of the user who posted content]
ORDER BY comment.id
LIMIT 20
以下是我在执行此操作时引用的主题:mysql/php: show posts and for each post all comments
问题是这真的很慢。我正在使用一个拥有超过200万个帖子,1500万条评论和大约500万用户的数据库。应该采用什么样的索引策略?有没有更好的方法来编写查询?是否有可能让查询在几秒钟内返回结果?这似乎有多慢或多快取决于用户的帖子数量。
非常感谢。
答案 0 :(得分:0)
<强>后续强>
我注意到在问题发布后2个月,OP增加了评论。我将拒绝重复我在原始答案(下面保留)中提出的观点,例如使用EXPLAIN
,创建适当的覆盖索引,以及向查询添加谓词。鉴于OP已经验证了这一点。
InnoDB缓冲池太小了#34;将导致性能问题,特别是如果有并发查询竞争池中的块,并导致磁盘读取。 (正如我之前提到的,&#34;使用filesort&#34;操作可能很昂贵(在资源和时间方面。)
考虑到大量的行和性能要求,我的目标是一个访问单个索引的查询计划,并避免使用&#34;使用filesort&#34;操作
此时,我正在寻求对数据模型进行非规范化以提高性能,以便我可以获得适当的索引。
鉴于post.author_fk
上有一个等同谓词,并且({有效)comment.id
上有一个降序范围扫描,我会考虑将这两列放到一个列中索引。
这意味着我要将post.author_fk
的值添加为comments
表中的列。
ALTER TABLE comments ADD post_author ...
当然,需要修改对comments
表执行INSERT / UPDATE的代码,以维护此列。 (更新整个表格会很痛苦,考虑到行数,我不会一举尝试。如果我必须这样做,我会在较小的一串中解决这个问题。
接下来,我在该表上添加一个索引:
CREATE INDEX comments_IX2 ON comments (post_author, id)
然后,我得到一个查询,将其用作覆盖索引,并尽快应用LIMIT 20。我们非常谨慎地介绍内联视图(因为它们可能是性能杀手),但在这种情况下,如果限制为20行,我会从一个查询开始,该查询可以快速获取感兴趣的评论中的行,使用索引扫描。
SELECT c.id
, c.post_fk
, c.author_fk
, c.text
FROM comments c
WHERE c.post_author = [id of the user who posted content]
ORDER BY c.post_author DESC, c.id DESC
LIMIT 20
该查询应该能够避免&#34;使用filesort&#34;通过对新索引使用降序扫描来进行操作。理想情况下,我们有覆盖索引,但考虑到我们需要返回text
列,维护更大的索引可能会更昂贵。我验证了这个查询运行良好,我将其包装在parens中,并将其作为内联视图包含在另一个查询中,如:
SELECT c.text
, u.name
, p.title
FROM ( SELECT c.id
, c.post_fk
, c.author_fk
, c.text
FROM comments c
WHERE c.post_author = [id of the user who posted content] -- new col
ORDER BY c.post_author DESC, c.id DESC -- new col
LIMIT 20
) t
JOIN user u
ON u.id = t.author_fk
JOIN post p
ON p.id = t.post_fk
ORDER BY t.id DESC
原始答案:如果查询返回了您想要的结果
(我怀疑你的意思是ORDER BY comment.id DESC
,假设id列是AUTO_INCREMENT,或者类似的升序值,其中最新的评论具有&#34;更高的&#34; id值。)
以下是我如何调整索引和查询计划。
首先,最重要的是,使用EXPLAIN来获取MySQL当前正在使用的查询计划。
其次,验证统计信息是最新的......在每个表上使用ANALYZE TABLE
。
查看查询,看起来我们肯定想要一些索引。我们希望在post
表上使用author_fk
作为前导列的索引,因为它是一个等式谓词,并且我们期望一些非常好的基数(我们期望这个谓词)通常会消除大量的行,并返回少于10%的行。
(如果这是InnoDB,并且这被定义为FOREIGN KEY约束,则已经存在适当的索引)。如果帖子中的行是公平的&#34;大&#34;,那么&#34;标题&#34;列通常相当小,然后我也倾向于在索引中包含该列,以便我们有覆盖索引。 (当索引满足查询而不引用基础表中的页面时,EXPLAIN输出将在'Using index'
列中显示&#34; Extra
&#34;这可以加快性能显著。)
由于查询还引用了id
列,因此可能还需要包含该列,但如果它是PRIMARY KEY(群集密钥),则该列值实际上可能已包含在index,作为&#34;指针&#34;回到表格中的那一行。
... ON post (author_fk, title, id)
由于comment
表的加入谓词位于post_fk
列,我们可能会想要
该列前导的索引。如果text
列占用注释表中的绝大部分空间,那么为此表创建覆盖索引可能也是有益的,但这可能不会对性能有多大好处。
... ON comment (post_fk)
我认为查询计划中最大的性能杀手就是排序操作。 (EXPLAIN输出可能会在'Using filesort'
列中显示Extra
。)
MySQL必须从满足谓词的comment
表中检索每一行,然后对该组行执行排序操作。
如果您可以添加其他一些谓词,例如您不希望评论日期超过7天,或类似的东西,则会减少需要排序的行数。
LIMIT子句几乎在查询计划中应用。因此,即使您只要求20行,MySQL实际上可能会排序成千上万行。
user
表的连接谓词已经在看起来像是主键。
(我假设id
列被定义为每个表的PRIMARY KEY的规范模式。)