4

我正在尝试提供一项功能,我可以在其中显示朋友查看次数最多的页面。我的朋友表有 570 万行,视图表有 530 万行。目前我只想在这两个表上运行一个查询,并找到一个人的朋友查看次数最多的 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),你可以看到它正在被使用。友谊表的主键为 (receiver_id, creator_id),二级索引为 (creator_id)。

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

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

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

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

更新:我需要对其进行更多测试,但如果我不在数据库中进行分组和排序,但之后在 ruby​​ 中进行,我的讨厌的查询似乎效果更好。总时间要短得多——似乎缩短了大约 80%。也许我的早期测试存在缺陷——但这绝对值得更多调查。如果是真的 - 那么 wtf 是 Mysql 在做什么?

4

3 回答 3

1

据我所知,进行“闪电般快速”的查询的最佳方法是创建一个汇总表,以跟踪每个创建者每页的朋友页面浏览量。

您可能希望通过触发器使其保持最新。那么你的聚合已经为你完成了,它是一个简单的查询来获得浏览次数最多的页面。您可以确保汇总表上有适当的索引,这样数据库甚至不必进行排序即可获得最多的浏览量。

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

如果您的统计数据不必完美,并且您的写入实际上相当频繁(可能是页面浏览量之类的情况),您可以在内存中批量处理视图并在后台处理它们,以便朋友们不要当他们查看页面时,不必费心使汇总表保持最新。该解决方案还减少了数据库争用(更新汇总表的进程更少)。

于 2009-06-17T01:46:53.377 回答
0

您的索引看起来是正确的,尽管如果friendship有非常大的行,您可能希望索引打开(creator_id, receiver_id)以避免读取所有内容。

但是这里有些不对劲,为什么要对 271 行进行文件排序?确保您的 MySQL 至少有几兆字节用于tmp_table_sizemax_heap_table_size. 这应该使 GROUP BY 更快。

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

于 2009-06-18T00:46:18.823 回答
0

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

您可以通过在视图表上使用触发器轻松维护此表,只要在“视图”表上发生插入,该触发器就会更新“视图摘要”表。

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

希望这可以帮助,

翻转

于 2009-06-17T01:49:00.003 回答