我试图找到一种直接的方法来提高非常活跃的论坛的性能,其中有大量帖子,mysql 不能再在内存中进行表排序,并且似乎没有充分利用索引。
这个简单的查询查找每个主题中的最新帖子,以便用户确定他们是否有任何回复(通过稍后比较 topic_time)
SELECT p.*, MAX(post_time) as post_time FROM forum_posts AS p
WHERE p.poster_id = '1' AND p.post_status = '0'
GROUP BY p.topic_id
ORDER BY post_time DESC
LIMIT 50
简单,扁平的桌子看起来像
post_id | poster_id | topic_id | post_status | post_time | post_text
然而,当有一百万个帖子并且用户自己有数万个帖子时,它的性能就会崩溃。MySQL 要么不再对内存中的表进行排序,要么扫描的行太多。在实际使用中可能需要长达 3 秒的时间,恕我直言,这是不可接受的,因为在此期间它会占用 CPU 并减慢其他所有人的速度。
我当然可以进行任何索引组合,但 mysql 似乎最喜欢使用组合
poster_id + post_time
所以它只是从百万中选择一个用户的 50k 帖子,然后开始按 topic_id 分组和排序。奇怪地将 topic_id 添加到索引组合中似乎对性能没有帮助,尽管它可能是索引字段的顺序?
我尝试编写一个等效的 JOIN,以便可以使用多个索引,但我遇到了问题,即每一侧都必须由 post_status 和海报过滤。
我在想它会更快,至少对于前几页,如果可以让mysql首先通过它的索引按post_time对数据进行排序,然后开始按降序为用户挑选不同的topic_id。我想这将需要一个子查询,并且不确定 50k 结果子查询会更好,仍然需要一个临时表。
当然,一个基本的解决方案是增加核心设计,以便有另一个表只存储每个主题中每个用户的最大 post_time ,但除非找不到其他解决方案,否则这是一个太大的变化。
感谢您的任何建议!
添加真实世界的示例并解释:
慢日志
# Query_time: 2.751334 Lock_time: 0.000056 Rows_sent: 40 Rows_examined: 48286
SELECT p.*, MAX(post_time) as post_time FROM forum_posts AS p WHERE p.poster_id = '2' AND p.post_status = '0' GROUP BY p.topic_id ORDER BY post_time DESC LIMIT 7000, 40;
解释
select_type table type possible_keys key key_len ref rows Extra
SIMPLE p ref poster_time poster_time 4 const 27072 Using where; Using temporary; Using filesort