今天有人跟我讲 MySQL 中 count(1) 比 count(*) 快,这能忍?必须得和他掰扯掰扯。
声明:以下操办基于 InnoDB 存储引擎,MyISAM 因为情况稀奇我在文末会单独说一下。
先说论断:这两个性能离别不大。
1.执行我准备了一张有 100W 条数据的表,表结构如下:
CREATE TABLE `user` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(255) DEFAULT NULL, `address` varchar(255) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
不错看到,有一个主键索引。
咱们来用两种样貌统计一下表中的记载数,如下:
不错看到,两条 SQL 的践诺效力其实差未几,都是 0.14s。
再来看另外两个统计:
id 是主键,username 以及 address 则是通俗字段。
不错看出,用 id 来统计,也有一丢丢上风。松哥这里因为测试数据样板相比小,是以恶果不剖析,小伙伴们不错加大测试数据量,那么这种各异会愈加剖析。
那么到底是什么原因形成的这种各异,接下来咱们就来通俗分析一下。
2. explain 分析咱们先用 explain 来看下这几个 SQL 不同的践诺权术:
不错看到,前三个统计样貌的践诺权术是通常的,后头两个是通常的。
我这里和公共相比下 explain 中的不同项:
type:前三个的 type 值为 index,暗意全索引扫描,等于把通盘索引过一遍就行(细巧是索引不是通盘表);后两个的 type 值为 all,暗意全表扫描,即不会使用索引。 key:这个暗意 MySQL 决定接纳哪个索引来优化对该表的探员,PRIMARY 暗意哄骗主键索引,NULL 暗意无谓索引。 key_len:这个暗意 MySQL 使用的键长度,因为咱们的主键类型是 INT 且非空,是以值为 4。 Extra:这个中的 Using index 暗意优化器只需要通过探员索引就不错取得到需要的数据(不需要回表)。通过 explain 咱们其实也能粗略看出来前三种统计样貌的践诺效力是要高一些的(因为用到了索引),尔后头两种的统计效力相对来说要低一些的(没用索引,需要全表扫描)。
仅有上头的分析还不够,咱们再来从旨趣角度来分析一下。
3. 旨趣分析 3.1 主键索引与通俗索引在驱动旨趣分析昔时,我想先指导公共看一下 B+ 树,这关于咱们意会接下来的内容有弥留作用。
公共都澄莹,InnoDB 中索引的存储结构都是 B+ 树(至于什么是 B+ 树,和 B 树有什么区别,这个本文就不操办了, 97视频这两个单独都能整出来一篇著述),主键索引和通俗索引的存储又有所不同,如下图暗意主键索引:
不错看到,在主键索引中,叶子结点保存了每一瞥的数据。
而在通俗索引中,叶子结点保存的是主键值,当咱们使用通俗索引去搜索数据的技能,先在叶子结点中找到主键,再拿着主键去主键索引中查找数据,绝顶于做了两次查找,这也等于咱们无为所说的回表操作。
3.2 旨趣分析不澄莹小伙伴们有莫得细巧过,咱们学习 MySQL 的技能,count 函数是归在团员函数那一类的,等于 avg、sum 等,count 函数和这些归在通盘,施展它亦然一个团员函数。
既然是团员函数,那么就需要对复返的收尾集进行一瞥行的判断,这里就波及到一个问题,婆岳同床双飞呻吟复返的收尾是啥?咱们差异来看:
关于 select count(1) from user; 这个查询来说,InnoDB 引擎会去找到一个最小的索引树去遍历(不一定是主键索引),但是不会读取数据,而是读到一个叶子节点,就复返 1,终末将收尾累加。
关于 select count(id) from user; 这个查询来说,InnoDB 引擎会遍历通盘主键索引,然后读取 id 并复返,不外因为 id 是主键,就在 B+ 树的叶子节点上,是以这个流程不会波及到就地 IO(并不需要回表等操作去数据页拿数据),性能亦然 OK 的。
关于 select count(username) from user; 这个查询来说,InnoDB 引擎会遍历整张表做全表扫描,读取每一瞥的 username 字段并复返,如果 username 在界说技能树立了 not null,那么班师统计 username 的个数;如果 username 在界说的技能莫得树立 not null,那么就先判断一下 username 是否为空,然后再统计。
终末再来说说 select count(*) from user; ,这个 SQL 的稀奇之处在于它被 MySQL 优化过,当 MySQL 看到 count(*) 就澄莹你是想统计总记载数,就会去找到一个最小的索引树去遍历,然后统计记载数。
因为主键索引(聚合索引)的叶子节点是数据,而通俗索引的叶子节点则是主键值,是以通俗索引的索引树要小一些。但是在上文的案例中,咱们唯有主键索引,是以最终使用的等于主键索引。
面前,如果我修改上头的表,为 username 字段也添加索引,然后咱们再来看 explain select count(*) from user; 的践诺权术:
不错看到,此时使用的索引等于 username 索引了,和咱们前边的分析收尾是一致的。
从上头的姿色中咱们就不错看出,第一个查询性能最高,第二个次之(因为需要读取 id 并复返),第三个最差(因为需要全表扫描),第四个的查询性能则接近第一个。
4. MyISAM 呢?可能有小伙伴澄莹,MyISAM 引擎中的 select count(*) from user; 操作践诺起来长短常快的,那是因为 MyISAM 把表中的行数班师存在磁盘中了,需要的技能班师读取出来就行了,是以相等快。
MyISAM 引擎之是以这么做,主如果因为它是不撑持事务的,是以它的统计骨子上就相等容易,添加一瞥记载一瞥就行了。
而咱们常用的 InnoDB 却不可这么做!为啥?因为 InnoDB 撑持事务!为了撑持事务,InnoDB 引入了 MVCC 多版块并发收尾,是以在数据读取的技能可能会有脏读、幻读以及不可重叠读等问题,具体不错参考 https://www.bilibili.com/video/BV14L4y1B7mB 视频。
是以,InnoDB 需要将每一瞥数据拿出来,判断该行数据对刻下会话是否可见,如果可见,就统计该行数据,不然不予统计。
虽然,MySQL 中的 MVCC 骨子上是一个相等巨大的话题,松哥以后有空了再和公共详备先容 MVCC。
好啦,面前小伙伴们懂了吧?有问题迎接留言操办。