目标:分析语句 select count(*) from table;
分析:
详解:
第一部分 count分析
1、show table status
Rows的值是根据采样估算(cardinality)获取的,不准确。
2、MyISAM引擎 count(*)
select count(*) from table
MyISAM引擎把一个表的总行数存储在磁盘上,执行count(*)的时候会直接读取,效率很高。
select count(*) from table where xxx=1
因为有了过滤条件,所以不能直接从磁盘读取,需要累计统计个数,效率一般。
备注:MyISAM引擎的主索引的叶子节点存储的是数据指针,二级索引的叶子节点存储的是数据指针,跟InnoDB引擎的主键索引是聚集索引,二级索引是辅助索引或覆盖索引不同,不会因为索引不同而导致效率不同。
3、InnoDB引擎 count(*)
select count(*) from table
都需要累计统计个数,效率一般。如果想提高效率,可以将总数存储在起来,存储在redis,mysql中都可以。
select count(*) from table where xxx=1
MySQL的InnoDB引擎的三个引擎
- 聚集索引:数据行是存储在叶子节点
- 辅助索引:即普通索引,叶子节点存储的主键id
- 覆盖索引:从辅助索引中就可以得到查询记录,例如,创建索引(name,age),查询语句为select age from table where name="xxx",这时候这个索引就属于覆盖索引。 统计数据都是需要把数据读取到内存然后处理,这样就知道不同索引读取到数据库的叶子节点的数据量是不同的,那么处理速度也就不同了。一般辅助索引会比聚集索引快。所以SQL语句的查询条件的好坏会影响查询速度。
第二部分 count对比
count()是怎么统计数据的?count()是一个聚集函数,对于返回的结果集,一行行地判断,如果count函数的参数不是NULL,累计值就加1,否则不加,最后返回累计值。
- count(*) : 专门做了优化,不取值,不是NULL,按行累加。
- count(1) : 不取值,判断1是不是NULL,按行累加。
- count(pk_id) : 取值id,判断id是不是NULL,按行累加。(不需要加载聚集索引的叶子节点数据)
- count(unique_key) : 取值unique_key,判断unique_key是不是NULL,按行累加。(不需要加载聚集索引的叶子节点数据)
- count(key) : 取值key,判断key是不是NULL,按行累加。(不需要加载聚集索引的叶子节点数据)
- count(no_key) : 取值no_key,判断no_key是不是NULL,按行累加。(需要加载聚集索引的叶子节点数据) 结论:count(no_key) < count(pk_id) ≈ count(unique_key) ≈ count(key) < count(1) < count(*)
第三部分 应用误区
平时,我们会遇到这样一个场景,根据某条件查询数据库中的数据 ''有'' 或 ''没有'' 两种状态时,大多写法为:
SELECT count(*) FROM table WHERE x = 1
在应用中判断返回值是否大于0。
这种写法好吗?我们分析一下。根据第二部分的分析,会取到所有的符合条件的数据,然后累加统计。根据业务场景描述,其实我们取到一个就可以了。如下
SELECT 1 FROM table WHERE x = 1 LIMIT 1
在应用中对返回值判空。
第四部分:扩展:Redis HyperLogLog
我们平常在使用MySQL时,当数据量达到一定值(例如千万级别),一般就会对MySQL分库分表了,也就是说我们上边讨论的数据范围一般不会超过千万级别的数据统计。那么问题来了,如果碰到一个网站日活,月活量特别大,需要记录唯一IP,并统计数量,我们该怎么办呢。根据第一部分的内容,有两种方法:
1、MySQL存储数据,redis统计数据。存在问题:数据可能会有偏差,可以接受,但是如果重启,需要重新统计初始值,耗时较长。
2、MySQL存储数据,MySQL统计数据。存在问题:数据可能会有偏差,可以接受,效率没有redis高。
综合来看,这2中方法都不是很好,那有没有更好的方法呢,肯定有了,嘿嘿,那我们就来看一下redis的HyperLogLog.
HyperLogLog是一个专门为了计算集合的基数而创建的概率算法,对于一个给定的集合,HyperLogLog可以计算出这个集合的近似基数:近似基数并非集合的实际基数,它可能会比实际的基数小一点或者大一点,但是估算基数和实际基数之间的误差会处于一个合理的范围之内,因此那些不需要知道实际基数或者因为条件限制而无法计算出实际基数的程序就可以把这个近似基数当作集合的基数来使用。
HyperLogLog的优点在于它计算近似基数所需的内存并不会因为集合的大小而改变,无论集合包含的元素有多少个,HyperLogLog进行计算所需的内存总是固定的,并且是非常少的。具体到实现上,Redis的每个HyperLogLog只需要使用12KB内存空间,就可以对接近:2^64个元素进行计数,而算法的标准误差仅为0.81%,因此它计算出的近似基数是相当可信的。
HyperLogLog不仅可以用于计数问题,还可以用于去重问题。
第五部分 总结
多理解原理