• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    MySQL中无过滤条件的count详解

    count(*)

    实现

    1、MyISAM:将表的总行数存放在磁盘上,针对无过滤条件的查询可以直接返回

    如果有过滤条件的count(*),MyISAM也不能很快返回

    2、InnoDB:从存储引擎一行行地读出数据,然后累加计数

    由于MVCC,在同一时刻,InnoDB应该返回多少行是不确定

    样例

    假设表t有10000条记录

    session A session B session C
    BEGIN;
    SELECT COUNT(*) FROM t;(返回10000)
    INSERT INTO t;(插入一行)
    BEGIN;
    INSERT INTO t(插入一行);
    SELECT COUNT(*) FROM t;(返回10000) SELECT COUNT(*) FROM t;(返回10002) SELECT COUNT(*) FROM T;(返回10001)

    最后时刻三个会话同时查询t的总行数,拿到的结果却是不同的

    InnoDB默认事务隔离级别是RR,通过MVCC实现

    优化

    1、InnoDB是索引组织表

    2、二级索引树占用的空间比聚簇索引树小很多

    3、优化器会在保证逻辑正确的前提下,遍历最小的索引树,尽量减少扫描的数据量

    show table status

    mysql> SHOW TABLE STATUS\G;
    *************************** 1. row ***************************
     Name: t
     Engine: InnoDB
     Version: 10
     Row_format: Dynamic
     Rows: 100256
     Avg_row_length: 47
     Data_length: 4734976
    Max_data_length: 0
     Index_length: 5275648
     Data_free: 0
     Auto_increment: NULL
     Create_time: 2019-02-01 17:49:07
     Update_time: NULL
     Check_time: NULL
     Collation: utf8_general_ci
     Checksum: NULL
     Create_options:
     Comment:

    SHOW TABLE STATUS同样通过采样来估算(非常不精确),误差能到40%~50%

    维护计数

    缓存

    方案

    缺点

    丢失更新

    1、Redis可能会丢失更新

    2、解决方案:Redis异常重启后,到数据库执行一次count(*)

    逻辑不精确 – 致命

    1、场景:显示操作记录的总数和最近操作的100条记录

    2、Redis和MySQL是两个不同的存储系统,不支持分布式事务,因此无法拿到精确的一致性视图

    时序A

    session B在T3时刻,查到的100行结果里面有最新插入的记录,但Redis还没有+1,逻辑不一致

    时刻 session A session B
    T1
    T2 插入一行数据R;
    T3 读取Redis计数;
    查询最近100条记录;
    T4 Redis计数+1;

    时序B

    session B在T3时刻,查到的100行结果里面没有最新插入的记录,但Redis已经+1,逻辑不一致

    时刻 session A session B
    T1
    T2 Redis计数+1;
    T3 读取Redis计数;
    查询最近100条记录;
    T4 插入一行数据R;

    数据库

    时刻 session A session B
    T1
    T2 BEGIN;
    表C中的计数值+1;
    T3 BEGIN;
    读表C计数值;
    查询最新100条记录;
    COMMIT;
    T4 插入一行数据R;
    COMMIT;

    count的性能

    语义

    1、count()是一个聚合函数,对于返回的结果集,一行一行地进行判断

    如果count函数的参数值不是NULL,累计值+1,否则不加,最后返回累计值

    2、count(字段F)

    3、count(主键ID)、count(1)、count(*)

    4、Server层要什么字段,InnoDB引擎就返回什么字段

    性能对比

    count(字段F)

    1、如果字段F定义为不允许为NULL,一行行地从记录里读出这个字段,判断通过后按行累加

    2、如果字段F定义为允许NULL,一行行地从记录里读出这个字段,判断通过后按行累加

    3、如果字段F上没有二级索引,只能遍历整张表(聚簇索引)

    4、由于InnoDB必须返回字段F,因此优化器能做出的优化决策将减少

    count(主键ID)

    count(1)

    1. InnoDB引擎会遍历整张表(聚簇索引),但不取值
    2. Server层对于返回的每一行,放一个数字1进去,判断是不可能为NULL,按行累加
    3. count(1)比count(主键ID)快,因为count(主键ID)会涉及到两部分操作

    count(*)

    1. count(*)不会把所有值都取出来,而是专门做了优化,不取值,因为『*』肯定不为NULL,按行累加
    2. 不取值:InnoDB返回一个空行,告诉Server层不是NULL,可以计数

    效率排序

    1. count(字段F) count(主键ID) count(1) ≈ count(*)
    2. 尽量使用count(*)

    样例

    mysql> SHOW CREATE TABLE prop_action_batch_reward\G;
    *************************** 1. row ***************************
     Table: prop_action_batch_reward
    Create Table: CREATE TABLE `prop_action_batch_reward` (
     `id` bigint(20) NOT NULL,
     `source` int(11) DEFAULT NULL,
     `serial_id` bigint(20) NOT NULL,
     `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
     `user_ids` mediumtext,
     `serial_index` tinyint(4) DEFAULT '0',
     PRIMARY KEY (`id`),
     UNIQUE KEY `uniq_serial_id_source_index` (`serial_id`,`source`,`serial_index`),
     KEY `idx_create_time` (`create_time`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8

    count(字段F)

    无索引

    user_ids上无索引,而InnoDB又必须返回user_ids字段,只能遍历聚簇索引

    mysql> EXPLAIN SELECT COUNT(user_ids) FROM prop_action_batch_reward;
    +----+-------------+--------------------------+------+---------------+------+---------+------+----------+-------+
    | id | select_type | table   | type | possible_keys | key | key_len | ref | rows | Extra |
    +----+-------------+--------------------------+------+---------------+------+---------+------+----------+-------+
    | 1 | SIMPLE | prop_action_batch_reward | ALL | NULL  | NULL | NULL | NULL | 16435876 | NULL |
    +----+-------------+--------------------------+------+---------------+------+---------+------+----------+-------+
    
    mysql> SELECT COUNT(user_ids) FROM prop_action_batch_reward;
    +-----------------+
    | count(user_ids) |
    +-----------------+
    | 17689788 |
    +-----------------+
    1 row in set (10.93 sec)

    有索引

    1、serial_id上有索引,可以遍历uniq_serial_id_source_index

    2、但由于InnoDB必须返回serial_id字段,因此不会遍历逻辑结果等价的更优选择idx_create_time

    mysql> EXPLAIN SELECT COUNT(serial_id) FROM prop_action_batch_reward;
    +----+-------------+--------------------------+-------+---------------+-----------------------------+---------+------+----------+-------------+
    | id | select_type | table   | type | possible_keys | key    | key_len | ref | rows | Extra |
    +----+-------------+--------------------------+-------+---------------+-----------------------------+---------+------+----------+-------------+
    | 1 | SIMPLE | prop_action_batch_reward | index | NULL  | uniq_serial_id_source_index | 15 | NULL | 16434890 | Using index |
    +----+-------------+--------------------------+-------+---------------+-----------------------------+---------+------+----------+-------------+
    
    mysql> SELECT COUNT(serial_id) FROM prop_action_batch_reward;
    +------------------+
    | count(serial_id) |
    +------------------+
    |  17705069 |
    +------------------+
    1 row in set (5.04 sec)

    count(主键ID)

    优化器选择了最优的索引idx_create_time来遍历,而非聚簇索引

    mysql> EXPLAIN SELECT COUNT(id) FROM prop_action_batch_reward;
    +----+-------------+--------------------------+-------+---------------+-----------------+---------+------+----------+-------------+
    | id | select_type | table   | type | possible_keys | key  | key_len | ref | rows | Extra |
    +----+-------------+--------------------------+-------+---------------+-----------------+---------+------+----------+-------------+
    | 1 | SIMPLE | prop_action_batch_reward | index | NULL  | idx_create_time | 5 | NULL | 16436797 | Using index |
    +----+-------------+--------------------------+-------+---------------+-----------------+---------+------+----------+-------------+
    
    mysql> SELECT COUNT(id) FROM prop_action_batch_reward;
    +-----------+
    | count(id) |
    +-----------+
    | 17705383 |
    +-----------+
    1 row in set (4.54 sec)

    count(1)

    mysql> EXPLAIN SELECT COUNT(1) FROM prop_action_batch_reward;
    +----+-------------+--------------------------+-------+---------------+-----------------+---------+------+----------+-------------+
    | id | select_type | table   | type | possible_keys | key  | key_len | ref | rows | Extra |
    +----+-------------+--------------------------+-------+---------------+-----------------+---------+------+----------+-------------+
    | 1 | SIMPLE | prop_action_batch_reward | index | NULL  | idx_create_time | 5 | NULL | 16437220 | Using index |
    +----+-------------+--------------------------+-------+---------------+-----------------+---------+------+----------+-------------+
    
    mysql> SELECT COUNT(1) FROM prop_action_batch_reward;
    +----------+
    | count(1) |
    +----------+
    | 17705808 |
    +----------+
    1 row in set (4.12 sec)

    count(*)

    mysql> EXPLAIN SELECT COUNT(*) FROM prop_action_batch_reward;
    +----+-------------+--------------------------+-------+---------------+-----------------+---------+------+----------+-------------+
    | id | select_type | table   | type | possible_keys | key  | key_len | ref | rows | Extra |
    +----+-------------+--------------------------+-------+---------------+-----------------+---------+------+----------+-------------+
    | 1 | SIMPLE | prop_action_batch_reward | index | NULL  | idx_create_time | 5 | NULL | 16437518 | Using index |
    +----+-------------+--------------------------+-------+---------------+-----------------+---------+------+----------+-------------+
    
    mysql> SELECT COUNT(*) FROM prop_action_batch_reward;
    +----------+
    | count(*) |
    +----------+
    | 17706074 |
    +----------+
    1 row in set (4.06 sec)

    参考资料

    《MySQL实战45讲》

    总结

    以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

    您可能感兴趣的文章:
    • MySQL 大表的count()优化实现
    • MySQL中聚合函数count的使用和性能优化技巧
    • 关于mysql中innodb的count优化问题分享
    • 聊聊MySQL的COUNT(*)的性能
    • 详解 MySQL中count函数的正确使用方法
    • 浅谈MySQL 统计行数的 count
    • mysql count提高方法总结
    • MySQL中count(*)、count(1)和count(col)的区别汇总
    • mySQL count多个表的数据实例详解
    • MySQL COUNT函数的使用与优化
    上一篇:MySQL中int最大值深入讲解
    下一篇:mysql 8.0.13手动安装教程
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    MySQL中无过滤条件的count详解 MySQL,中无,过滤,条件,的,