count(ID)怎么就成慢SQL了?

版权声明:如有转载-请加微信457556886告知下就可以 知识都是分享的 https://blog.csdn.net/wolf_love666/article/details/87986846

1、慢SQL报告,超过0.1s统计

在这里插入图片描述

2、背景前提:

ID,主键
activity_ID和applicant联合索引。
status无索引
数据库引擎:InnoDB
created:普通字段,无索引

3、单纯SQL优化方向考虑几方面?

1、SQL优化,比如不使用like,not in,join,distinct等
2、索引是否使用正确
3、库表的主键ID是否设计合理

其他考虑方面:
引擎:
数据库引擎的选择
有序主键
分离日志和文件位置到其他磁盘防止和存储引擎争用
内存:
缓冲池的设计
缓存池命中率
减少IO次数:
索引片和过滤因子的使用是否正确
索引:
最左原则
三星原则
联合索引
冗余索引

4、测试数据统计比较

在这里插入图片描述
在这里插入图片描述
最终结果:

count(*)>count(1)>count(常量)>count(ID)
count(1)>count(*)>count(常量)>count(ID)

5、InooDB的执行引擎原理

在这里插入图片描述在这里插入图片描述

SQL解析原理:

假如有个sql,如select, where, order by, group by

select xx from xx where xx and xx order by xx limit xx;
那么我们知道首先解析的顺序为
from xx

发出信号确认是否有该表

where xx and xx

第一颗星需要取出所有等值谓词中的列,作为索引开头的最开始的列(任意顺序);不只是将等值谓词的列加入索引,它的作用是减少索引片的大小以减少需要扫描的数据行

order by xx

第二颗星需要将 ORDER BY 列加入索引中;避免排序,减少磁盘 IO 和内存的使用
第三颗星需要将查询语句剩余的列全部加入到索引中;避免每一个索引对应的数据行都需要进行一次随机 IO 从聚集索引中读取剩余的数据;

limit xx

过滤因子筛选出符合条件的

select xx

有了上面的思路回到现在的这个问题

explain解析下查询SQL
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

从explain解释执行查询语句返回的结果来看,确实是一致的。但是查询的时间我们知道确实不一致。
上面的参数可以参考这个:
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
然后可以通过如下方式来探究:
set profiling =1;
继续执行查询语句
show profiling;
show profile cpu ,block io for query XX(id值)
我这里可以以视图方式展现
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
从上面看我们确实没有优化的建议了。。。。。那么是真的么?
当然不是。

count(*)>count(1)>count(常量)>count(ID)
count(1)>count(*)>count(常量)>count(ID)

这里回到原来的问题,为什么会出现,引用下探讨
在这里插入图片描述
当引擎发现你的col不可能为null的时候,它会将col转换成星号,如果用的id是主键,那么其实会被转化成星号,这转化也是耗费性能的,也就是说常量和ID都会转化成*号,而这段时间是微不足道但是也计算在内的。

MySQL查询优化器目标就是为了减少SQL的执行时间,那为什么会用二级索引而不去用主键索引呢,因为在统计行数的操作中涉及到磁盘IO问题,降低磁盘IO问题就大大的减少执行时间。IO带宽是一定的,索引占用的空间越小产生的IO次数就越少。而innodb的主键索引包括key,事务id和rollpointer,而二级索引包括key和主键id,所以使用二级索引的开销会比较少。所以innodb的select count( )操作一般都是通过二级索引来进行统计操作
InnoDB的主键索引采用聚簇索引存储,使用的是B+Tree作为索引结构,但是叶子节点存储的是key和数据本身。
InnoDB的二级索引不使用聚蔟索引,叶子节点存储的是KEY和主键值。因此,通过二级索引查询首先查到是主键值,然后InnoDB再根据查到的主键值通过主键索引找到相应的数据块。
count(*)和count(1)操作会统计表中列的行数,包括NULL列,count(col)操作会统计指定列的行数,不包括NULL值。

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
探讨结束
大体的意见就是我们测试的意见。

count(*)>count(1)>count(字段)>count(ID)
count(1)>count(*)>count(字段)>count(ID)

为什么呢?我更偏向于提到的字段是否判空,之后都再转成count(*);

如果有人有更好的想法可以告知我!


2018-02-28更新
根据上面感觉是对着的。我又有了一种解答思路:
由于ID是主键,而我的联合索引为activity_id和applicant.那么我们需要知道一点就是每次查询IO是以page为单位可能4/8/16/32/64kb吧。不同机器不一样。那么这里面就有个索引片,每次根据activity_id和applicant和status计算出索引因子获得。然后呢?然后是相当于这个图:
在这里插入图片描述主键列 id 在所有的 MySQL 索引中都是一定会存在的。(这句话是有道理的)
当我没有id时候,使用count(created)通过联合索引确定了集合,那么这个时候他不需要解析id但是需要判断created是否为空
而count(id)的时候,他可能是仗着自己是主键,会遍历整张表,这样的基数范围变大了,索引片每次查询次数就会增多,然后也是需要判断每次主键是否为空再累加。

回到本文要解决的问题:
超时count
原因如前提条件那里提到的,status是普通字段,而这个字段整个表里面有2种类型的值。
那么根据解析SQL来看的话,联合索引之后,status即为过滤列进行再度筛选。
那么是否将该三个字段都加为联合索引会提高效率呢?这里要探讨的就是过滤列VS联合索引的问题,哪个更好。

猜你喜欢

转载自blog.csdn.net/wolf_love666/article/details/87986846