【问题标题】:Select top n rows efficiently有效地选择前 n 行
【发布时间】:2017-06-08 16:07:09
【问题描述】:

所以我有一个表格,可能有数百万行,

user   | points
---------------
user1  | 10
user2  | 12
user3  | 7
...

SELECT * FROM mytable ORDER BY points LIMIT 100, 1000 现在它工作正常,但速度非常慢(在大表上),因为它拒绝使用任何类型的索引,而是执行全表扫描。我怎样才能提高效率?

我的第一个(显而易见的)想法是在 points DESC 上使用索引,但后来我发现 MySQL 根本不支持这些。

接下来,我尝试反转点上的符号,这意味着本质上在 - 点上有一个升序索引,这也没有帮助,因为它不使用索引进行排序

最后,我尝试使用force index,这几乎没有提高性能,因为它仍然获取整个表,但没有排序(在EXPLAIN 中使用filesort: false)

我确信这一定是一个已解决的问题,但我没有在网上找到任何有用的信息。任何提示将不胜感激。

【问题讨论】:

  • 如果您的表中有“数百万行”,那么对于您正在运行的硬件而言,您的数据库可能太大了。然而,结构本身可能与它有很大关系。您的数据库只是用户/点吗?如,主键是 varchar?还是?
  • 这个'top n'怎么样?
  • @Strawberry 我认为 MySql 不支持top n,是吗?
  • 前 10 名 = ORDER BY points DESC LIMIT 10
  • 阅读这篇文章,尤其是关于性能问题的部分。 meta.stackoverflow.com/questions/271055/…

标签: mysql


【解决方案1】:

从查询中获得更好性能的一些方法。

从不使用SELECT *。这是一个菜鸟的错误。它基本上告诉查询规划器它需要为您提供一切。 总是在结果集中枚举你想要的列。这是您想要的查询(假设您没有过度简化您的问题)。

 SELECT user, points
   FROM table
  ORDER BY points
  LIMIT 100,1000

使用复合索引。对于您的查询,(points, user) 上的复合索引将允许使用部分索引扫描来满足您的查询。这应该比全表排序更快。 MySQL可以向后或向前扫描索引,所以你不必担心降序

要添加正确的索引,请使用这样的命令。

ALTER TABLE table ADD INDEX points_user (points, user);

编辑。反对在此处使用SELECT * 的建议是基于(1)我未经证实的怀疑,即所讨论的表过于简化并且在现实生活中还有其他列,以及(2)有时索引必须与查询精确匹配的不便现实获得最佳性能结果。

根据经验,我坚持自己的观点,即在对性能敏感的查询中使用 SELECT * 并不是好的工程实践(除非您非常喜欢该查询,以至于想要一次又一次地返回它)。

【讨论】:

  • “它基本上告诉查询规划器它需要给你一切” --- everything 什么?计划者肯定知道有 2 列,因此您的查询在查询优化器如何看待它方面是相同的。
  • @zerkms 这也是我的思路,但显然它确实有所作为。无论如何,使用复合索引似乎已经为我解决了这个问题!
  • @CBenni 这是一个 mysql 覆盖索引,不是吗?我不敢相信 SELECT *SELECT user, points 在这种特殊情况下会改变任何事情。
  • 关于这件事有很多讨论(select * vs select columns),我喜欢的一个是this one
  • @JorgeCampos 与select * 相比,它与通过显式枚举选择相同的列无关。那些只提到选择更少的列,这在直观上更有效(要传输的东西更少)
猜你喜欢
  • 1970-01-01
  • 2012-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-11
  • 2013-10-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多