共计 1670 个字符,预计需要花费 5 分钟才能阅读完成。
I、count 实现形式(无 where 条件时)
MyISAM 引擎:把一个表的总行数存在了磁盘上,执行 count 时间接返回,效率很高;
InnoDB 引擎:把数据一行一行地从引擎外面读出来,而后累积计数。
1、有 where 条件时,MyISAM 和 InnoDB 实现形式一样
2、因为 MVCC(多版本并发管制),InnoDB 引擎即便是在同一个时刻的多个查问,“应该返回多少行”也是不确定的。3、InnoDB 默认隔离级别可反复读,代码上通过 MVCC 实现。每一行记录都要判断本人是否对这个会话可见。4、InnoDB 是索引组织表,主键索引树的叶子节点是数据,而一般索引树的叶子节点是主键值。所以,一般索引树比主键索引树小很多。对于 count 操作,遍历哪个索引树失去的后果逻辑上都是一样的。因而,MySQL 优化器会找到最小的那棵树来遍历。在保障逻辑正确的前提下,尽量减少扫描的数据量,是数据库系统设计的通用法令之一。
II、count 毛病
MyISAM 尽管 count 很快,然而不反对事务;
show table status 命令尽管返回很快,然而不精确;
InnoDB 间接 count 会遍历全表,尽管后果精确,但会导致性能问题。
因而须要本人实现。
III、用缓存零碎保留计数 - 应用 Redis 服务保留表的总行数
1、将计数保留在缓存零碎中的形式,还不只是失落更新的问题。即便 Redis 失常工作,这个值还是逻辑上不准确的。
这里次要起因是“MySQL 插入一行数据”跟“Redis 计数加 1”这两个操作是离开的,不是原子性的,这就很可能在两头过程因为某些并发呈现问题。更形象一点:MySQL 和 Redis 是两个不同的载体,将关联数据记录到不同的载体,而不同载体要实现原子性很难,因为不是原子性很容易引起并发问题。如果能将数据对立在同个载体即 MySQL,并由其保障操作的原子性,行将插入一行数据和计数加 1 作为一个残缺的事务,通过事务的隔离此时外界看到的就是要么全副执行结束要么全副都没执行,进而放弃逻辑统一。
2、在并发零碎外面,咱们是无奈准确管制不同线程的执行时刻的。
IV、在数据库保留计数 - 计数间接存储到数据库里独自的一张计数表
1、在数据库中建表计数,能够失去精准的计数,办法是通过数据库中的事务来实现的。计数器的批改和数据的写表在一个事务中。读取计数器和查问最近数据也在一个事务中。
2、解决办法:将计数的记录 + 1 和插入一条数据放入到同一个事务中。
V、count
1、count() 是一个聚合函数,对于返回的后果集,一行行地判断,如果 count 函数的参数不是 NULL,累计值就加 1,否则不加。最初返回累计值。
2、对于 count(主键 id) 来说,InnoDB 引擎会遍历整张表,把每一行的 id 值都取出来,返回给 server 层。server 层拿到 id 后,判断是不可能为空的,就按行累加。
3、对于 count(1) 来说,InnoDB 引擎遍历整张表,但不取值。server 层对于返回的每一行,放一个数字“1”进去,判断是不可能为空的,按行累加。
4、对于 count(字段) 来说:
——如果这个“字段”是定义为 not null 的话,一行行地从记录外面读出这个字段,判断不能为 null,按行累加;
——如果这个“字段”定义容许为 null,那么执行的时候,判断到有可能是 null,还要把值取出来再判断一下,不是 null 才累加。
5、count()并不会把全副字段取出来,而是专门做了优化,不取值。count() 必定不是 null,按行累加。
依照效率排序的话,依照效率排序的话,count(字段)<count(主键 id)<count(1)≈count(*)。所以尽量应用 count(*)。
VI、剖析性能差异准则:
1、server 层要什么就给什么;
2、InnoDB 只给必要的值;
3、当初的优化器只优化了 count(*) 的语义为“取行数”,其余“不言而喻”的优化并没有做。
VII、温习 -InnoDB 特点
事务反对: redolog 持久性,undolog 原子性,mvcc+ 锁隔离级别。
并发:行锁而不是简略的表级锁。
数据安全:数据要长久化到磁盘