美文网首页
Innodb存储引擎count()计数性能比较

Innodb存储引擎count()计数性能比较

作者: Tod_2021 | 来源:发表于2022-07-29 22:00 被阅读0次

    0.前言

    MySQL中有九种存储引擎,其中主要的两种引擎分别是:

    • MyISAM
    • InnoDB

    MySQL在5.5版本之前的默认引擎为MyISAM,从5.5版本开始默认的存储引擎为InnoDB

    MyISAM不支持事务特性,而InnoDB支持事务。

    • 查看引擎

      mysql>SHOW ENGINES;
      Engine              Support     Transactions
      ------------------  -------     ------------
      FEDERATED           NO          (NULL) 
      MRG_MYISAM          YES         NO
      MyISAM              YES         NO
      BLACKHOLE           YES         NO
      CSV                 YES         NO
      MEMORY              YES         NO
      ARCHIVE             YES         NO
      InnoDB              DEFAULT     YES
      PERFORMANCE_SCHEMA  YES         NO
      
    • 如何查看指定数据库的表结构

      mysql>USE shop;
      mysql>SHOW TABLE STATUS;
      Name          Engine  Version  Row_format    Rows  Avg_row_length
      ------------  ------  -------  ----------  ------  --------------
      shop_order    InnoDB       10  Compact         23             712
      shop_product  InnoDB       10  Compact          4            4096
      shop_user     InnoDB       10  Compact          2            8192
      

    MyISAM存储引擎中,会把一个表的总行数存储在磁盘上。而InnoDB存储引擎由于多版本并发控制(MVCC)机制,对于count(*)应该返回多少行并不确定,只能把数据一行一行从引擎中读出,然后累积计数,无法将总行数存储在磁盘中。

    1.InnoDB引擎count(*)计数

    1.1存在问题

    假设表t中现在有 10000 条记录,我们设计了三个用户并行的会话。

    • 会话 A 先启动事务并查询一次表的总行数;
    • 会话 B 启动事务,插入一行后记录后,查询表的总行数;
    • 会话 C 先启动一个单独的语句,插入一行记录后,查询表的总行数。

    我们假设从上到下是按照时间顺序执行的,同一行语句是在同一时刻执行的。

    数据库事务会话A、B、C的执行过程

    你会看到,在最后一个时刻,三个会话 A、B、C 会同时查询表 t 的总行数,但拿到的结果却不同。

    因为InnoDB引擎的默认隔离级别是可重复读,在代码上就是通过多版本并发控制(MVCC)实现的,每一行记录都要判断是否对本会话(Session)是否可见,对于count(*)请求来说,只能把数据一行一行读出依次判断。

    1.2数据库保存计数

    由于在InnoDB的可重复读隔离级别下,每一个会话创建的视图不一定一致,导致count(*)计数时结果的不一致,这也就是为什么InnoDB引擎不能像MyISAM引擎将表的总行数固定写到磁盘中(MyISAM不支持事务特性)。那么解决这一问题的方式大致可以分为两种:

    • 利用缓存系统保存计数
    • 利用数据库表保存计数

    其中利用缓存系统(如Redis)保存计数,由于不能支持事务的强一致性,同样会导致数据不一致的结果,具体例子如下:

    缓存系统会话A、B的执行过程

    此时,会话B并不能查询到新插入的数据R,但是计数值已经加了1。虽然Redis能正常工作,但是这个计数值在逻辑上是不精确的。

    而利用数据库的事务特性就能实现逻辑上的一致性,如下图所示

    数据库事务会话A、B的执行过程

    由于InnoDB默认隔离级别为可重复读,由于会话B在会话A未提交事务之前开启了事务,所以会话A开启的事务视图与会话B开启的事务视图是一致的,同时会话A的计数值加1操作对于会话B也是不可见的,这就能实现会话B查询最近100条记录在逻辑的一致性。

    2.InnoDB引擎下count()性能比较

    count():返回对应列值不为null的行数

    2.1count(*)

    由于聚簇索引下(即主键索引)的索引树每个叶子节点都是整行数据,而非聚簇索引的索引树叶子节点存储的是索引值,所以Server层优化器对于count()进行计数时,会选择非聚簇索引中最小的索引树来遍历。*并不取值进行判断。

    2.2count(1)

    InnoDB引擎会遍历整张表,但不取值。Server层对于返回的每一行,放入一个数字”1“进去。判断不可能为空,按行累加

    2.3count(主键id)

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

    count(1) 执行得要比 count(主键 id) 快。因为从引擎返回 id 会涉及到解析数据行,以及拷贝字段值的操作

    2.4count(字段)

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

    查找效率:count(*) ≈ count(1) > count(主键id) >= count(字段)

    相关文章

      网友评论

          本文标题:Innodb存储引擎count()计数性能比较

          本文链接:https://www.haomeiwen.com/subject/ovfqwrtx.html