14 | count(*)这么慢,我该怎么办?

一、抛砖引玉
MySQL的count(*) 语句到底是怎样实现的,以及 MySQL 为什么会这么实现?

二、正文开始

🏁:1.这个问题回答之前需要从不同的数据库引擎分析

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

⚠️⚠️⚠️:你要是加了where条件MyISAM也懵逼,不会返回这么快

👋:2.为什么Innodb不把数字事先存起来呢?
因为即使是在同一个时刻的多个查询,由于多版本并发控制(MVCC)的原因,InnoDB 表“应该返回多少行”也是不确定的。
举例:
在这里插入图片描述

show table status 命令的话,就会发现这个命令的输出结果里面也有一个 TABLE_ROWS 用于显示这个表当前有多少行,这个命令执行挺快的,那这个 TABLE_ROWS 能代替 count(*) 吗?
不能!!! 因为索引统计的值是通过采样来估算的,不准的概率达到40-50%

🏁:3.这时候如果用缓存计数会有什么问题?不用缓存还有什么方案可以替代。
用缓存系统保存计数有丢失数据和计数不精确的问题。那么,如果我们把这个计数直接放到数据库里单独的一张计数表 C 中,又会怎么样呢?
在这里插入图片描述
🏁:4.面试题来了!!!
count(*)、count(主键 id)、count(字段) 和 count(1) 等不同用法的性能,有哪些差别?

⭐️:非性能差别。
4.1.count(*)、count(主键 id) 和 count(1) 都表示返回满足条件的结果集的总行数;而 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才累加。
    ⚠️⚠️⚠️:这里没有分析字段上有没有索引,**如果字段上没有加索引。count(字段)会比count(id)慢!**因为,count(id)可能会选择最小的索引来遍历,而count(字段)的话,如果字段上没有索引,就只能选主键索引

  • 但是 count() 是例外,并不会把全部字段取出来,而是专门做了优化,不取值。count() 肯定不是 null,按行累加。

所以结论是:
count(字段):遍历整张表,需要取值,判断 字段 != null,按行累加;
count(id) :遍历整张表,需要取ID,判断 id !=null,按行累加;
count(1) : 遍历整张表,【不需要】取值,返回的每一行放一个数字1,按行累加;
count() : 【不需要取字段】,count(),按行累加; 因为count(*) 和 count(1) 不取字段值,减少往 server层的数据返回,所以比其他count(字段)要返回值的【性能】较好;

三、思考题
由于事务可以保证中间结果不被别的事务读到,因此修改计数值和插入新记录的顺序是不影响逻辑结果的。但是,从并发系统性能的角度考虑,你觉得在这个事务序列里,应该先插入操作记录,还是应该先更新计数表呢?

先插入新纪录,因为插入新记录只会影响到行锁和间隙锁,而先更新计数表会占用计数表的写锁,而很多其他事务的插入操作就必须阻塞等待

四、评论中的精华
🏁:5.MySQL 什么是幻读?如何解决?
文章中的案例中并没有幻读现象
请看下一篇文章分析

[https://blog.csdn.net/hxy_lbj/article/details/115125950](14-01 | MySQL 幻读如何解决?)

👋:对于 count(主键 id) ,server层拿到ID,判断ID是不可能为空的按行累加。这个地方,是不是又点问题,既然是主键ID,是一定不会为空的,这个server层还需要判断不为空吗
答:代码就是这么写的 我也觉得可以优化一下… 不过现在就这样

可以通过以下步骤使用 Zabbix 监控 MySQL 服务器: 1. 在 MySQL 服务器上安装 Zabbix Agent。可以从 Zabbix 官网下载适用于您的操作系统的 Zabbix Agent 安装包。 2. 在 MySQL 服务器上配置 Zabbix Agent。修改 Zabbix Agent 配置文件(zabbix_agentd.conf),添加以下配置项: ``` UserParameter=mysql.status[*],mysql -e "show global status where Variable_name='$1'" | awk 'NR==2 {print $$2}' UserParameter=mysql.size[*],echo "SELECT ROUND((SUM(data_length + index_length) / 1024 / 1024), 2) AS MB FROM information_schema.TABLES WHERE table_schema = '$1';" | mysql | awk 'NR==2 {print $$1}' UserParameter=mysql.slave[*],mysql -e "show slave status\G" | grep "$1:" | awk '{print $$2}' | sed 's/,$$//' UserParameter=mysql.connections,mysql -e "show status like 'Threads_connected'" | awk 'NR==2 {print $$2}' UserParameter=mysql.users,mysql -e "select count(*) from mysql.user" | awk 'NR==2 {print $$1}' UserParameter=mysql.qps,mysqladmin -uroot -p'password' extended-status | grep -w 'Queries' | awk '{print $$4}' UserParameter=mysql.slowqueries,mysqladmin -uroot -p'password' status | awk '{print $$14}' UserParameter=mysql.deadlocks,mysql -e "SHOW ENGINE INNODB STATUS" | grep -A 2 "LATEST DETECTED DEADLOCK" | tail -n 1 | awk '{print $$1}' ``` 其中,`mysql.status[*]` 用于获取 MySQL 服务器的状态信息,`mysql.size[*]` 用于获取 MySQL 数据库的大小,`mysql.slave[*]` 用于获取 MySQL 主从复制延迟时间,`mysql.connections` 用于获取 MySQL 连接数,`mysql.users` 用于获取 MySQL 用户数,`mysql.qps` 用于获取 MySQL 执行的增、删、改、查的次数,`mysql.slowqueries` 用于获取 MySQL 执行的查询次数,`mysql.deadlocks` 用于获取 MySQL 产生的死锁数量。具体的配置项可以根据需要进行修改。 3. 在 Zabbix Server 上添加 MySQL 服务器的监控项。在 Zabbix Server 上登录管理界面,创建新的主机并绑定 MySQL 服务器的 IP 地址,然后添加对应的监控项,例如:`mysql.status[Innodb_buffer_pool_read_requests]`、`mysql.size[dbname]`、`mysql.slave[Seconds_Behind_Master]`、`mysql.connections`、`mysql.users`、`mysql.qps`、`mysql.slowqueries`、`mysql.deadlocks`。 4. 在 Zabbix Server 上创建触发器和图形。使用 Zabbix 的触发器功能可以设置当 MySQL 服务器出现异常时自动发送警报通知管理员;使用 Zabbix 的图形功能可以实时展示 MySQL 服务器的状态信息。 通过以上步骤,就可以使用 Zabbix 监控 MySQL 服务器的进程工作状态、数据目录剩余空间、二进制日志目录剩余空间、IO线程工作状态、SQL线程工作状态、主从复制延迟时间、MySQL连接数、MySQL用户数、MySQL执行的增、删、改、查的次数、MySQL产生的死锁数量和MySQL执行的查询次数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值