• 面试官:count(*)、count1(1)、count(主键)、count(字段) 哪个更快?

面试官:count(*)、count1(1)、count(主键)、count(字段) 哪个更快?

2025-08-16 12:33:07 栏目:宝塔面板 0 阅读

大家好,我是君哥。

使用 SQL 时,统计表数据量是经常遇到的需求,比如在商品页面展示商品库存量。

统计表数据量的方法有 count(*)、count1(1)、count(主键 id)、count(字段)。那这些统计方式哪个执行最快呢?以 MySQL 数据库为参考,今天来聊一聊这个话题。

count(*)

首先 ,count(*) 返回的是检索到的行数,无论数据行里是否包含空值。

如果使用的是 MyISAM 存储引擎,MyISAM 会把表的数据行数保存下来存到磁盘,因此执行 count(*) 的时候不用统计,而是直接把保存的结果查出来,效率最高。但这有几个前提条件:

  1. 不能带 where 条件;
  2. 只能从单表查询;
  3. 查询 SQL 不能有额外的字段;
  4. MyISAM 不支持事务,可能会有数据不一致的情况。

如果使用的是 InnoDB 存储引擎,count 就是一个聚合函数,对返回的结果集逐行进行判断,只要不是 NULL 就加 1,效率不如 MyISAM。当然,MySQL 对 count(*) 做了优化,会选择数据量最小的二级索引进行扫描,以提高执行效率。

count(1)

对于 count(1) 来说,InnoDB 引擎遍历整张表,server 层对于返回的每一行,不用取值,直接放一个数值 1 进去,然后计数值加 1。

count(主键 id)

对于 count(主键 id) ,InnoDB 引擎会遍历整张表,把每一行的 id 值取出来,返回给 server 层。server 层拿到 id 后,判断主键 id 不可能为空,计数值加 1。

count(字段)

对于 count(字段) 来说:如果字段定义成 not null,server 层逐行判断,只要引擎层返回的记录不为空,计数值加 1;如果这个字段定义成 null,那server 层拿到记录后,还需要取出值判断是不是 null,如果值不是 null,计数值加 1。

小结

对于 InnoDB 引擎来说,使用 count(*) 和 count(1) 是一样的,没有性能区别。下面是官网解释。

InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.

count(*) 比 count(主键 id) 快,因为执行 count(主键 id) 的时候,引擎层需要解析主键 id 的值返回给 server 层。

count(字段) 性能最差,因为引擎层解析字段值返回给 server 层后,server 层需要从行记录取出值进行判断是否为 NULL。

缓存

那有更快的方案查询表数据量吗?有。可以考虑使用 redis 缓存来。在 redis 中建 一个 key 来保存数据量,当表插入新数据时,缓存数据量 + 1。

但使用缓存可能会有两个问题:

  1. 会有数据不一致得情况,不适用于精确统计的场景;

比如上图,插入数据后,还没有来得及更新缓存,已经有应用查询了缓存的 count 值。

可以考虑把缓存的 count 值写入 MySQL 数据库,使用 InnoDB 引擎的事务来保证一致性。

2. redis 服务宕机后,会丢失数据。这个可以在 redis 重启后重新从数据库查询最新 count 数来写入缓存。

总结

  1. 如果数据量不大,比如不到百万级别,或者对 count 值精确度要求很高,可以直接使用 count(*) 获取行数。
  2. 如果数据量非常大,查询频率也很高,可以考虑缓存 count 值;
  3. 如果考虑使用缓存,但业务又要求 count 值精确,那就把 count 值缓存在数据库中;
  4. 如果对 count 精确度要求不高,可以考虑把 count 值缓存在 redis;
  5. 如果要统计某一个字段非空的行数,则使用 count(字段);
  6. 如果是分库分表的场景,则采用并行统计来提高效率。

本文地址:https://www.yitenyun.com/333.html

搜索文章

Tags

数据库 API FastAPI Calcite 电商系统 MySQL Web 应用 异步数据库 数据同步 ACK 双主架构 循环复制 Deepseek 宝塔面板 Linux宝塔 Docker 生命周期 JumpServer JumpServer安装 堡垒机安装 Linux安装JumpServer 序列 核心机制 esxi esxi6 root密码不对 无法登录 web无法登录 SSL 堡垒机 跳板机 HTTPS Windows Windows server net3.5 .NET 安装出错 宝塔面板打不开 宝塔面板无法访问 查看硬件 Linux查看硬件 Linux查看CPU Linux查看内存 HTTPS加密 连接控制 机制 Windows宝塔 Mysql重置密码 无法访问宝塔面板 Oracle 处理机制 HexHub 运维 Serverless 无服务器 语言 Spring Redis 异步化 ES 协同 技术 group by 索引 InnoDB 数据库锁 开源 PostgreSQL 存储引擎 监控 分页查询 高可用 SQL 动态查询 响应模型 缓存方案 缓存架构 缓存穿透 自定义序列化 R edis 线程 数据 主库 服务器 管理口 scp Linux的scp怎么用 scp上传 scp下载 scp命令 日志文件 MIXED 3 SVM Embedding 查询 存储 OB 单机版 Linux 安全 工具 电商 系统 SQLark SQLite-Web SQLite 数据库管理工具 架构 Rsync Postgres OTel Iceberg 云原生 RocketMQ 长轮询 配置 Recursive 共享锁 流量 • 索引 • 数据库 防火墙 黑客 修改DNS Centos7如何修改DNS sftp 服务器 参数 向量数据库 大模型 聚簇 非聚簇 PG DBA 优化 万能公式 缓存 业务 AI 助手 Ftp ​Redis 机器学习 推荐模型 场景 GreatSQL 连接数 Netstat Linux 服务器 端口 数据备份 MySQL 9.3 RDB AOF openHalo 数据分类 加密 同城 双活 信息化 智能运维 INSERT COMPACT 窗口 函数 人工智能 推荐系统 核心架构 订阅机制 线上 库存 预扣 事务 Java 开发 MVCC 磁盘架构 redo log 重做日志 向量库 Milvus mini-redis INCR指令 网络架构 网络配置 Doris SeaTunnel prometheus Alert Redisson 锁芯 高效统计 今天这篇文章就跟大家 Undo Log Python B+Tree ID 字段 模型 引擎 性能 网络故障 PostGIS IT运维 Canal Web Redis 8.0 不宕机 数据脱敏 加密算法 R2DBC 分布式 集中式 崖山 新版本 Hash 字段 自动重启 Pottery 虚拟服务器 虚拟机 内存 OAuth2 Token JOIN Entity ZODB 微软 SQL Server AI功能 容器化 数据类型 MongoDB 数据结构 DBMS 管理系统 容器 sqlmock 启动故障 读写 LRU SpringAI 悲观锁 乐观锁 分库 分表 QPS 高并发 传统数据库 向量化 数据页 StarRocks 数据仓库 数据集成工具 分页 单点故障 工具链 Testcloud 云端自动化 Redka 分页方案 排版 filelock 排行榜 排序 发件箱模式 事务隔离 部署 聚簇索引 非聚簇索引 1 大表 业务场景 意向锁 记录锁 速度 服务器中毒 Web 接口 SSH 日志 原子性 池化技术 连接池 Caffeine CP 仪表盘 分布式架构 分布式锁​ dbt 数据转换工具 EasyExcel MySQL8 AIOPS MCP 开放协议 InfluxDB 优化器 网络 双引擎 RAG HelixDB Order IT 频繁 Codis Go 数据库迁移 字典 Ansible 单线程 Crash 代码 对象 LLM 订单 线程安全 事务同步 Pump List 类型 UUIDv7 主键 服务器性能 锁机制 行业 趋势 TIME_WAIT 负载均衡 千万级 恢复数据 分布式锁 CAS 播客 MGR 失效 多线程 技巧 Weaviate Zookeeper 分布式集群 解锁 调优 并发控制 恢复机制 ReadView 闪回 国产数据库 产业链 兼容性 Next-Key 国产 用户 快照读 当前读 视图 RR 互联网 关系数据库 编程 算法 数据字典 主从复制 代理 GitHub Git 语句 count(*) count(主键) 行数 UUID ID 神经系统 慢SQL优化 表空间 拦截器 动态代理 Valkey Valkey8.0 查询规划 矢量存储 数据库类型 AI代理