首页 > 解决方案 > 在索引列上计算行数比在主列和普通列上慢?

问题描述

我想知道为什么会发生这种情况?我有一个简单的表,其中包含一个主键Id列、一个索引列A和一些其他普通列(of datetime),并且所有字段都具有非空值。

当我尝试像这样计算主键列上的行数时:

select count(Id) from my_table

返回值大约需要 0.4 秒(总共大约 1.1M 条记录)。

我尝试了相同的查询,但对于普通(datetime如前所述)列,它几乎需要相同的时间(实际上有点慢)。但是当我在索引列上尝试相同的查询时A,最多需要 1.2 秒才能返回计数:

select count(A) from my_table

A索引信息(如果您需要检查):

type:        BTREE
Allows NULL: Yes
Unique:      No
Packed:     (empty)

你能给我一些解释这个问题吗?我们能做些什么来改善它吗?我不能指望其他列,因为实际上我必须清楚地计算该列,因此返回的计数对每一列都有不同的含义。

标签: mysqlcountquery-optimization

解决方案


最快的是COUNT(*)。这*是一个约定;它并不意味着“所有列”。 COUNT(1)是等价的。

COUNT(col)当您要排除存在的任何col行时使用NULL。(这很少需要。)如果col是声明NOT NULL的,那么包含确实是一种浪费col。(注意:我说的是声明的;你说的是不同的:有非空值。)根据Allows NULL: Yes,你声明了列NULL,因此COUNT(col) 检查每一个col

会发生什么COUNT(*)

  • 对于没有 a 的计数WHERE,优化器会选择“最小”索引并遍历它。PRIMARY KEY总是(?)最大的,所以它通常被回避。这个算法的原因是它假设它必须从磁盘读取整个索引;I/O 很慢;而“更小”意味着更少的 I/O。
  • 如果有WHERE子句,则最佳索引取决于WHERE子句中的内容——这本身就是一个巨大的话题。

这是一个好主意,NOT NULL除非您有业务逻辑需要NULLs(“尚未指定”、“可选”、“已删除”、“不适用”,...)有几种情况(大多数是晦涩难懂的)NOT NULL表现略好于NULL.

警告:我的答案适用于 InnoDB,但不完全适用于 MyISAM。


推荐阅读