背景

我们经常会使用到一个SQL语句,就是查询某张表的总行数。常常使用的查询命令有几种,比如:select count(*) from tselect count(id) from t(id为主键)select count(1) from tselect count(某普通字段) from t以及show table status的rows字段。然而却不知道用哪种查询方式最合适。接下来简单介绍一下我们在MySQL中查询表的总行数时该用什么命令。

不同存储引擎,查询效率不同

在分析几种查询方式之间的不同之前,我们先来看下以select count(*) from t为例,不同存储引擎之间查询行数方式的不同。常见的存储引擎有MyISAM和InnoDB。

InnoDB:每次我们需要查询某张表的总行数时,都会遍历整张表计算出结果。

MyISAM:每张表的总行数会记录在磁盘中存储,在我们需要查询某张表的总行数时,命令会直接取出对应字段信息。(不过需要注意,当命令查询的不是表总行数,而是where查询某些行时,也依然需要像InnoDB一样遍历整张表计算出结果)

很显然,在表行数较多时,MyISAM的方式是要比InnoDB更快的。

那么,为什么InnoDB不采用与MyISAM相同的处理方式呢?

本质原因在于InnoDB支持事务,我们可以看个具体的例子就明白了。

假设我们的表t中包含了100行数据,且有如下三个会话在做SQL操作。(会话2与会话3是事务,会话1是普通语句)
在这里插入图片描述
先不管中间是怎样的过程,我们看最后三个会话输出的查询结果。

如果是MyISAM存储引擎,它是不支持事务的,查询出的结果三个会话就都是102。

而如果是InnoDB存储引擎,它是支持事务的,可重复读是默认的隔离级别,所以查询出的结果三个会话各不相同,会话1是101,会话2是100,会话3是102。

那么,为什么InnoDB不采用与MyISAM相同的处理方式呢?这就很明显了。因为在相同时刻,InnoDB由于支持事务,所以可能会读出的结果不同,

因此,InnoDB只能在需要查询表总行数时去遍历计算了。

MyISAM由于不存在这样的问题,当然就可以把总行数存储下来,每次需要时直接读取就行了。

几个常用查询语句的效率不同

说回到开始说的几个常用查询语句,我们到底该用哪个查询语句呢?我们以InnoDB引擎为例进行说明。

首先,我们要了解一下count()函数的作用,count()函数会判断所查询字段是否为NULL,会将非NULL的行累加起来,得到最终的行数值。

接下来分别分析这几个常用查询语句:

select count(字段) from t(字段为非NOT NULL且非索引):服务器会将字段返回给服务器,服务器判断每一个字段内容是否为NULL,如果不为NULL,则会计数加1,累加出来的结果即为语句结果返回。

select count(id) from t(id为主键):InnoDB会将每一行id返回给服务器,服务器判断主键不可能为NULL,累加出来的结果即为语句结果返回。

select count(1) from t:InnoDB会遍历整张表,不过不会取字段,也不会取值。而是服务器放一个数字1进去,并将累加出来的值作为结果返回。

select count(*) from t:优化器针对该语句做了优化,InnoDB不需要取字段给服务器,而是服务器直接计算行总数并返回。

由于 count(*)count(1) 都无需从InnoDB中取值,而是直接计算行总数,则两者运行时间差不多。

count(id) 会取出id值进行判断,虽然主键值都不会为NULL,但运行时间会比前两者长一些。

count(字段)count(id) 判断方式一样,只是其不是索引,所以遍历起来较count(id) 会慢。

总结来说,四种常用查询方式的速度顺序为:count(字段)<count(id)<count(1)<count(*)

注意:select count(id) from t语句,虽然id为主键不可能是NULL值,理论上来说,MySQL是无需一个一个再进行数值判断的。然而目前MySQL并没有对此做优化,应该是在查询总行数时,MySQL就建议使用select count(*) from t,因此专门对count(*) 做了优化。其余的查询就都按照需要什么数据则取什么数据这样的原则统一处理。

show table status命令

show table status命令中的Rows字段也会显示出表的总行数,但是该命令是通过采样统计来计算出的表总行数,因此会有很大的误差。作为参考值还可以,如果要使用精确值,就不推荐了。

总结

简单总结一下,我们在查询某张表总行数时,如果需要知道大体行数,则可使用show table status的rows字段查看。如果需要知道精确行数,则需使用select count(*) from t命令来查看。

Logo

更多推荐