孔乙己显出极高兴的样子,将两个指头的长指甲敲着柜台,点头说,“对呀对呀!……回字有四样写法,你知道么?”我愈不耐烦了,努着嘴走远。孔乙己刚用指甲蘸了酒,想在柜上写字,见我毫不热心,便又叹一口气,显出极惋惜的样子。
写下这个标题,立刻就联想到了孔乙己,于是找出了上文的引用。闲话不表,说正事。
count查询一般在统计满足某条件的数据总数时会用到,常见的就是数据统计分析和分页查询的场景。
这篇文章的目的主要是想研究一下count的用法,找到使用这个方法的最佳实践。
实现
查询引擎差异
总所周知,mysql是基于多种存储引擎的数据库,不同的存储引擎之间实现差异很大,在myisam引擎下,将总行数直接保存了下来,而innodb没有这样做。
之所以innodb不保存也很好理解,innodb有事务隔离,在不同的事务内,统计到的数据行数是可能存在差异的。
估算方法
统计行数的方法其实并不止这一种,show table status
语句也能够查询到每张表的数据行数,差别在于,count方法得到的是精确值,而使用这种方法拿到的是一个估算的值,在官方文档的描述中提到,这种估算方法计算出来的值误差能达到50%,所以如果日常使用,只能提供对数据量级上的估算,在单表数据量大的时候也许有点用、
那么他又是怎么实现的呢?
数据库中存在描述表信息的元数据,mysql存放元数据的库叫information_schema
,这个库下的TABLES
表里有个字段叫TABLE_ROWS
,用这个字段来保存估算的数据行数。
select TABLE_ROWS from information_schema.'TABLES' WHERE TABLE_NAME = 'xxxx'
使用查询验证了一下,两者的值一致,说明确实是取的这里的值
性能
先说结论,性能上
count(*) = count(1) >= count(primary key) >= count(非空字段) >= count(可空字段)
几种查询方式各自的实现:
语句 | 实现 |
---|---|
count(1) | 遍历整张表,不取值,累加行数 |
count(primaryKey) | 遍历整张表,把主键值拿出来,累加行数 |
count(非空字段) | 遍历整张表,读出这个字段,累加 |
count(可空字段) | 遍历整张表,读出这个字段,判断不为空后累加 |
count(*) | 遍历整张表,数据库层面进行了优化,不会把字段全部取出来,按行累加 |
阿里的java开发手册上将count(*)
作为推荐用法,书里给出的理由:
【强制】不要使用 count(列名)或 count(常量)来替代 count(),count()是 SQL92 定义的标
准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
验证
虽然根据资料得出了关于性能差异的结论,但是还是想自己找张表验证一下结论。
找了张6w数据的表尝试使用不同的count语句进行查询,发现性能差异并不明显,也许是数据量太小,反正在我的测试下几乎没有差异。
参考资料
https://segmentfault.com/a/1190000022262561
https://juejin.cn/post/6844903974445776903
https://www.zhihu.com/question/34781415
https://www.modb.pro/db/48242