我使用的MySQL
表包含以下数据:
users(ID, name, email, create_added) (about 10000 rows)
points(user_id, point) (about 15000 rows)
我的疑问:
SELECT u.*, SUM(p.point) point
FROM users u
LEFT JOIN points p ON p.user_id = u.ID
WHERE u.id > 0
GROUP BY u.id
ORDER BY point DESC
LIMIT 0, 10
我只获得前10名用户的最佳分数,但随后就死了。如何改善查询的性能?
答案 0 :(得分:2)
与@Grim相同,您可以使用INNER JOIN
代替LEFT JOIN
。但是,如果您真的想要进行优化,我建议您在表users
添加一个预先计算point
的额外字段。使用您当前的数据库设计,此解决方案可以胜过任何查询优化。
答案 1 :(得分:1)
为LEFT JOIN
交换INNER JOIN
会有很大帮助。确保将points.point
和points.user_id
编入索引。我假设您可以删除WHERE
子句,因为u.id
将始终大于0(尽管MySQL可能会在查询优化阶段为您执行此操作)。
答案 2 :(得分:1)
除了你只得到10行并不重要。 MySQL必须在为每个用户排序之前总结点数(“使用filesort”操作。)LIMIT最后应用。
覆盖索引ON points(user_id,point)
将是获得最佳效果的最佳选择。 (我真的只是猜测,没有任何EXPLAIN
输出或表定义。)
id
中的users
列可能是主键,或者至少是唯一索引。因此,您可能已经有一个以id
为首要列的索引,或者如果它是InnoDB则为主键群索引。)
我很想测试这样的查询:
SELECT u.*
, s.total_points
FROM ( SELECT p.user_id
, SUM(p.point) AS total_points
FROM points p
WHERE p.user_id > 0
GROUP BY p.user_id
ORDER BY total_points DESC
LIMIT 10
) s
JOIN user u
ON u.id = s.user_id
ORDER BY s.total_points DESC
这确实有创建派生表的开销,但是在点上有一个合适的索引,前导列为user_id,并且包括point列,MySQL很可能通过使用索引优化组,并避免一个“使用filesort”操作(对于GROUP BY)。
可能会对该结果集执行“使用filesort”操作,以获取按total_points排序的行。然后从那里获得前10行。
使用这10行,我们可以加入到用户表中以获取相应的行。
但是......这个结果有一点点差别,如果前10位中user_id
的任何值都不在用户表中,则此查询将返回少于10行。 (我希望有一个外键定义,所以这不会发生,但我真的只是猜测没有表定义。)
EXPLAIN
将显示MySQL正在使用的访问计划。
答案 3 :(得分:0)
有没有想过分区? 我目前正在使用大型数据库并成功改进了SQL查询。
例如,
PARTITION BY RANGE (`ID`) (
PARTITION p1 VALUES LESS THAN (100) ENGINE = InnoDB,
PARTITION p2 VALUES LESS THAN (200) ENGINE = InnoDB,
PARTITION p3 VALUES LESS THAN (300) ENGINE = InnoDB,
... and so on..
)
它允许我们在扫描mysql表时获得更好的速度。即使表中有数百万行,Mysql也只扫描包含用户ID 1到99的分区p 1。