【问题标题】:Clustered index on integer column surprisingly slow整数列上的聚集索引出奇地慢
【发布时间】:2021-08-05 18:13:11
【问题描述】:

我有一个包含 750,000 条记录的 InnoDB 表。它的主键是BIGINT

当我这样做时:

SELECT COUNT(*) FROM table;

需要 900 毫秒。 explain 表示没有使用索引。

当我这样做时:

SELECT COUNT(*) FROM table WHERE pk >= 3000000;

需要 400 毫秒。 explain 表明在这种情况下使用了索引。

我希望在x >= pk >= y 的位置进行快速计数。

据我了解,由于我使用表的主键,因此我使用的是聚集索引,因此行(物理上?)由该索引排序。那么做这个计数不应该非常非常快吗?我原以为结果会在十几毫秒左右提供。

我已经读过,如果我只选择表格的一小部分,可以预期更快的结果。然而,我对做这些范围计数很感兴趣。也许我应该以不同的方式组织我的数据?

在另一种情况下,我有一个包含空间数据的表并使用 RTREE 索引,然后我使用 MBRContains 来计算匹配行(以及二级索引)。令人惊讶的是,这比上面的简单情况要快。

【问题讨论】:

  • 您好用 COUNT(1) 代替 COUNT(*) 并在 pk 字段中使用聚集索引
  • 经过的时间可能高度依赖于该表或索引是否被缓存的事实。您应该始终重复测量。此外,只有在实现索引快速全扫描访问方法时,计数才能从大范围的索引访问中受益,我不确定 InoDB 的情况
  • @RahulBiswas 这是个好主意。不过,MySQL 开发人员的想法相同,因此(对于 InnoDB),count(1) 和 count(*) 完全相同。
  • 对于 InnoDB,数据存储在“主键”中。读取整个表和使用主键读取整个表是一回事。

标签: mysql indexing


【解决方案1】:

在 InnoDB 中,PRIMARY KEY 与数据“聚集”在一起。这意味着数据按 PK 排序,where pk BETWEEN x AND y 必须读取从xy 的所有行。

那么,它是如何进行 PK 扫描的呢?它必须读取数据块。它们体积庞大,因为它们还有其他列。

但是没有WHERECOUNT(*) 呢?在这种情况下,优化器会查找体积最小的索引并计算其中的行数。所以...

如果您有二级索引,它将使用它。

如果你只有PK,那么它会读取整个表来做计数。

也就是说,在最窄的列上人为添加二级索引很可能会加速SELECT COUNT(*) FROM tbl

但是等等...确保每次计时测试运行两次。第一次(重新启动后)必须从磁盘读取所需的块。慢。

第二次所有块都可能位于 RAM 中。更快。

SPATIALFULLTEXT 索引使这个讨论变得复杂。特别是如果您有 2 个部分到 WHERE,一个带有空间或全文,一个带有常规测试。

COUNT(1)COUNT(*) 是相同的。 COUNT(x) 在将行包含在计数中之前检查 x 是否为 NOT NULL

【讨论】:

  • 全部正确,但where x >= pk => y 根本无法按预期工作。它将第一次比较与布尔值(0 或 1)进行比较,然后将该值与y 进行比较。最好使用where pk between y and x
  • @BillKarwin - 好吧,这不是坏语法,而是“它没有做你认为应该做的事情”。我改写了我的答案以在不使用该表达的情况下表达我的观点。
  • @RickJames 感谢您的精彩解释。您的回答让我自己找到了其他类似问题的答案,因此我对 count 的工作原理有了更好的了解。
  • 是的,有时我听起来像是“破纪录”。
猜你喜欢
  • 1970-01-01
  • 2012-08-26
  • 1970-01-01
  • 1970-01-01
  • 2010-11-17
  • 2023-03-23
  • 2013-12-02
  • 2019-02-13
  • 2015-04-16
相关资源
最近更新 更多