关于MySQL的count

关于MySQL的count

感谢丁奇,让我们对MySQL的count了解的更清楚了。

count(*), count(1),count(主键id) 以及count(字段)哪个快?

原则:

  1. server 层要什么就给什么;
  2. InnoDB只给必要的值;
  3. 优化器只优化了count(*)的语义为”取行数“。

count(*), count(1),count(主键id) 都表示返回满足条件的结果集的总行数;而count(字段)则表示返回满足条件的数据行里面,参数“字段”不为NULL的总个数。

count(主键id):InnoDB引擎会遍历整张表,把每一行的id值都取出来,返回给server层。server层拿到id后,判断是不可能为空,就按行累加。

count(1): InnoDB引擎会遍历整张表,但不取值。server层对于返回的每一行,放一个数字1进去,判断不可能为空,按行累加。可以看出count(1)执行要比count(主键id)快。因为从引擎返回id会涉及到解析数据行,以及拷贝字段操作。

count(字段):

  • 如果这个“字段”是定义为not null, 一行行地从记录里面读出这个字段,判断不能为null, 按行累加。
  • 如果这个“字段”定义为云讯null, 那么执行的时候,判断到有可能是null, 还要把取值取出来再判断下,不是null才累加。

count(*):
并不会把全部字段取出来返回给server层,而是做了专门优化,不取值。count(*)肯定不是null, 所以操作只是:遍历全表,按行累加。

结论是:按照效率排序的话,

1
count(字段) < count(主键id) < count(1) < ~= count(*)

建议尽量使用count(*)。

count(*) 实现方式

不同的引擎中,count(*)有不同的实现方式。

  • MyISAM引擎把一个表的总行数存在了磁盘上,因此执行count(*)的时候回直接返回这个数,效率很高。
  • 而InnoDB引擎因为要支持事务的多版本并发控制MVCC,执行count(*)的时候,需要把数据一行行读出来,然后累积计数。

优化取数业务

如果业务使用了InnoDB引擎,业务上还需要频繁显式记录总数,例如一个页面要经常显示交易系统的操作记录总数。应该如何优化? – 只能自己计数,找地方把操作记录表的行数存起来。

使用缓存

使用缓存会出现逻辑上不一致的问题。例如:

会话A是一个插入交易记录的逻辑,往数据表里插入一行R,然后Redis计数加1;会话B就是查询页面显示时需要的数据。在上图的时序里,T3时刻会话B查询的时候,会显示出新插入的R这个记录,但是Redis的技术还没加1.这时候会出现数据不一致。 – 两个不同的存储构成的系统,不支持分布式事务,无法拿到精确一致的视图。

使用DB保存计数

把计数直接放到数据库里面单独的一张计数表,利用事务是可以实现一致的优化取数业务的。

虽然会话B的读操作仍然是在T3执行的,但是因为这时候新事物还没有提及,所以计数值加1这个操作对会话B还不可见。

因此会话B看到的结果里,查计数和最近100条记录看到的寄过,逻辑上就是一致的。

参考

MySQL实战45讲


  MySQL

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×