热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

高性能MySQLcount(1)与count(*)的差别

 -----------------------------------------------------------------------------------------

 

-------------------------------------------------------------------------------------------------第一篇-------------------------------------------------------------------------------------------------------------------

 

sql调优,主要是考虑降低:consistent gets和physical reads的数量。

count(1)与count(*)比较:

如果你的数据表没有主键,那么count(1)比count(*)快
如果有主键的话,那主键(联合主键)作为count的条件也比count(*)要快
如果你的表只有一个字段的话那count(*)就是最快的啦
count(*) count(1) 两者比较。主要还是要count(1)所相对应的数据字段。
如果count(1)是聚索引,id,那肯定是count(1)快。但是差的很小的。
因为count(*),自动会优化指定到那一个字段。所以没必要去count(?),用count(*),sql会帮你完成优化的

count详解:

count(1)count(主键)一样,只扫描主键。

count(*)count(非主键)

  count(*)将返回表格中所有存在的行的总数包括值为null的行,然而count(列名)将返回表格中除去null以外的所有行的总数(有默认值的列也会被计入).
  distinct 列名,得到的结果将是除去值为null和重复数据后的结果

count(主键) 不一定比count(其余索引) 快: 

索引是一种b+树的结构,以块为单位进行存储。假设块大小是1k,主键索引大小为4b,有一个字段a的索引大小为2b。 
同样一个块,能存放256个主键索引,但是能存放512个字段a的索引。 
假设总数据是2k条,意味着主键索引占用了8个块,而a字段索引占用了4个块,统计时用主键索引需要经历的块多,io次数多。效率也比a字段索引慢。


总结

 

1.如果在开发中确实需要用到count()聚合,那么优先考虑count(*),因为mysql数据库本身对于count(*)做了特别的优化处理。

有主键或联合主键的情况下,count(*)略比count(1)快一些。 
没有主键的情况下count(1)比count(*)快一些。 
如果表只有一个字段,则count(*)是最快的。
2.使用count()聚合函数后,最好不要跟where age = 1;这样的条件,会导致不走索引,降低查询效率。除非该字段已经建立了索引。使用count()聚合函数后,若有where条件,且where条件的字段未建立索引,则查询不会走索引,直接扫描了全表。 
3.count(字段),非主键字段,这样的使用方式最好不要出现。因为它不会走索引.

 

--------------------------------------------------------------------------------第二篇----------------------------------------------------------------------------------------------------------------

 

首先,以我们最常见的两种数据库表引擎myisam和innodb来讲。


myisam

myisam在统计表的总行数的时候会很快,但是有个大前提,不能加有任何where条件。这是因为:myisam对于表的行数做了优化,具体做法是有一个变量存储了表的行数,如果查询条件没有where条件则是查询表中一共有多少条数据,myisam可以做到迅速返回,所以也解释了如果加where条件,则该优化就不起作用了。细心的同学会发现,innodb的表也有这么一个存储了表行数的变量,但是很遗憾这个值是一个估计值,没有什么实际意义


innodb

在该引擎下,count(1)和count(*)哪个快呢?结论是:这俩在高版本的mysql(5.5及以后,5.1的没有考证)是没有什么区别的,也就没有coun(1)会比count(*)更快这一说了。

why?这就要从count()函数的具体含义说起了。”


 count()有两个非常不同的作用:它可以统计某个列值的数量,也可以统计行数。在统计列值时要求列值是非空的(不统计null)。如果在count()的括号中定了列或者列表达式,则统计的就是这个表达式有值的结果数。......count()的另一个作用是统计结果集的行数。当mysql确认括号内的表达式值不可能为空时,实际上就是在统计行数。最简单的就是当我们使用count(*)的时候,这种情况下通配符*并不像我们猜想的那样扩展成所有的列,实际上,他会忽略所有列而直接统计所有的行数“——《高性能mysql》。

通常,我们将第一个字段(一般是id)作为主键,那么这个时候count(1)实际统计的就是行数(此处表达有误,详见文章结尾),因为主键肯定是非null的。问题是innodb是通过主键索引来统计行数的吗?结论是:如果该表只有一个主键索引,没有任何二级索引的情况下,那么count(*)和count(1)都是通过通过主键索引来统计行数的。如果该表有二级索引,则count(1)和count(*)都会通过占用空间最小的字段的二级索引进行统计,也就是说虽然count(1)指定了第一列(此处表达有误,详见文章结尾)但是innodb不会真的去统计主键索引(一般为第一个字段的索引)。


实验


第一步

新建一张基于innodb的表,只有一个id主键,并插入5w的测试数据,建表语句如下:

create table `tb_news` (
`id` bigint(21) not null auto_increment,
`title` varchar(50) not null,
`content` mediumtext not null,
`count_ass` char(1) not null,
primary key (`id`)
) engine=innodb auto_increment=50001 default charset=utf8

这个时候执行count(1)和count(*)可以看到解释器的结果如下(两者一致,所以就只截了一张图),可以看到,两者都用了主键索引进行行数的统计:



第二步

新建一个二级索引title,之后在分别看一下count(1)和count(*)的解释器结果(两者依然完全一致),这时已经用二级索引进行统计而非主键索引:



第三步

在我们之前特地预留的一个小字段count_ass字段建一个索引,到这一步目前表中有三个索引:一个主键索引,两个二级索引。

 


这时候我们再看一下count(1)和count(*)会通过哪个索引来统计行数(两者还是一致)。

 



原理

目前基于磁盘的数据库或者搜索引擎(比如lucene)的性能瓶颈主要都是在io阶段,相比于cpu和ram,io操作实在太慢了,所以这类系统的优化方向也都都是类似的——尽一切可能减少io的次数(所以很多用es的程序在性能优化到极限的时候选择直接上ssd)。这里统计行数的操作,查询优化器的优化方向就是选择能够让io次数最少的索引,也就是基于占用空间最小的字段所建的索引(每次io读取的数据量是固定的,索引占用的空间越小所需的io次数也就越少)。而innodb的主键索引是聚簇索引(包含了key,除了key之外的其他字段值,事务id和mvcc回滚指针)所以主键索引一定会比二级索引(包含key和对应的主键id)大,也就是说在有二级索引的情况下,一般count()都不会通过主键索引来统计行数,在有多个二级索引的情况下选择占用空间最小的

如果说有张innodb的表只有主键索引,而且记录还比较大(比如30k),则统计行的操作会非常慢,因为io次数会很多(这里就不做实验截图了,有兴趣可以自己试一下)。

一个优化方案就是预先建一个小字段并建二级索引专门用来统计行数,极端情况下这种优化速度提高上千倍也是正常的。


结论

结论就是对于count(1)和count(*)执行优化器的优化是完全一样的,并没有count(1)会比count(*)快这个说法



推荐阅读
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
author-avatar
雨润风华_684
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有