优化两个大表的简单查询

时间:2009-06-17 01:27:18

标签: mysql performance optimization query-optimization

我正在尝试提供一个功能,我可以显示朋友最常查看的页面。我的friends表有5.7M行,views表有5.3M行。目前我只想对这两个表运行一个查询,并找到一个人的朋友查看的最多20个页面ID。

以下是我现在的查询:

SELECT page_id 
FROM `views` INNER JOIN `friendships` ON friendships.receiver_id = views.user_id 
WHERE (`friendships`.`creator_id` = 143416) 
GROUP BY page_id 
ORDER BY count(views.user_id) desc 
LIMIT 20

以下是解释的内容:

+----+-------------+-------------+------+-----------------------------------------+---------------------------------+---------+-----------------------------------------+------+----------------------------------------------+
| id | select_type | table       | type | possible_keys                           | key                             | key_len | ref                                     | rows | Extra                                        |
+----+-------------+-------------+------+-----------------------------------------+---------------------------------+---------+-----------------------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | friendships | ref  | PRIMARY,index_friendships_on_creator_id | index_friendships_on_creator_id | 4       | const                                   |  271 | Using index; Using temporary; Using filesort | 
|  1 | SIMPLE      | views       | ref  | PRIMARY                                 | PRIMARY                         | 4       | friendships.receiver_id                 |   11 | Using index                                  | 
+----+-------------+-------------+------+-----------------------------------------+---------------------------------+---------+-----------------------------------------+------+----------------------------------------------+

views表的主键为(user_id,page_id),您可以看到正在使用它。 friendships表的主键为(receiver_id,creator_id),辅助索引为(creator_id)。

如果我在没有group by和limit的情况下运行此查询,则此特定用户大约有25,000行 - 这是典型的。

在最近的实际运行中,这个查询也需要7秒执行,这对于在Web应用程序中获得合适的响应来说太长了。

我想知道的一件事是我应该将二级索引调整为(creator_id,receiver_id)。我不确定这会带来多大的性能提升。我今天可能会根据这个问题的答案来尝试。

你能看到任何方式可以重写查询以使其快速闪亮吗?

更新:我需要对它进行更多测试,但是如果我不在数据库中进行分组和排序,那么看起来我讨厌的查询会更好,但之后会在ruby中进行。整体时间要短得多 - 看起来大约80%。也许我的早期测试存在缺陷 - 但这肯定需要更多的调查。如果这是真的 - 那么wtf是Mysql在做什么?

3 个答案:

答案 0 :(得分:1)

据我所知,制作类似“闪电般快速”的查询的最佳方法是创建一个摘要表,用于跟踪每个创建者每页的朋友页面浏览量。

您可能希望将其与触发器保持同步。然后,您的聚合已经为您完成,这是一个简单的查询,以获取查看次数最多的页面。您可以确保在摘要表上有适当的索引,这样数据库甚至不必排序以获得最多的查看。

摘要表是在大多数读取环境中保持聚合类型查询的良好性能的关键。当更新发生时(不频繁),然后查询(频繁)不需要做任何工作,您可以预先完成工作。

如果您的统计数据不一定非常完美,并且您的写入实际上非常频繁(页面视图可能就是这种情况),您可以在内存中批量处理视图并在后台处理它们,以便当他们查看页面时,朋友们不必为了使摘要表保持最新。该解决方案还减少了对数据库的争用(更新摘要表的流程更少)。

答案 1 :(得分:0)

你应该绝对看看这个表的非规范化。如果您创建一个单独的表来维护用户ID以及他们查看的每个页面的确切计数,您的查询应该变得更加简单。

您可以使用视图表上的触发器轻松维护此表,只要在“视图”表上发生插入操作,就会对“views_summary”表进行更新。

您甚至可以通过查看实际关系来进一步对此进行非规范化,或者只维护每人的前x个页面

希望这有帮助,

埃弗特

答案 2 :(得分:0)

您的索引看起来正确,但如果friendship行数非常大,您可能希望(creator_id, receiver_id)上的索引避免读取所有索引。

然而,有些东西不在这里,为什么你要为271行做一个文件存储? 确保MySQL的tmp_table_sizemax_heap_table_size至少有几兆字节。这应该使GROUP BY更快。

sort_buffer也应该有一个合理的价值。