MySQL语句执行过程及count(*)效率问题

SQL的执行过程

下面我给出的是MySQL的基本架构示意图,并且清楚的展示了SQL语句的执行过程:

image-20230412141059794
  • 连接器:连接器负责跟客户端建立连接、获取权限、维持和管理连接。客户端如果太长时间没动静,连接器就会自动将它断开。
  • 查询缓存:将之前查询的结构进行缓存,key是查询语句。但是MySQL 8.0版本直接将查询缓存的整块功能删掉了。
  • 分析器:对SQL语句先进行词法分析再进行语法分析
  • 优化器:会对语句进行优化判断。
  • 执行器:先校验权限,如果校验通过就会根据表的引擎定义,去使用这个引擎提供的接口,最后将结果组成结果集返回给客户端。

count(*)这么慢

你首先要明确的是,在不同的MySQL引擎中,count(*)有不同的实现方式:

  • MyISAM引擎把一个表的总行数存在了磁盘上,因此执行count(*)的时候会直接返回这个数,效率很高;
  • InnoDB引擎就麻烦了,它执行count(*)的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数。

由于多版本并发控制(MVCC)的原因,InnoDB表“应该返回多少行”也是不确定的

按照效率排序的话,count(字段)<count(主键id)<count(1)≈count(*),所以我建议你,尽量使用count(*)

其他方式:

  1. 用缓存系统保存计数

    在并发系统里面,我们是无法精确控制不同线程的执行时刻的,因为存在图中的这种操作序列,所以,我们说即使Redis正常工作,这个计数值还是逻辑上不精确的。

  2. 在数据库保存计数

    把这个计数直接放到数据库里单独的一张计数表C中,并利用利用“事务”这个特性把问题解决掉。