新特新解读 | MySQL 8.0 对 count(*)的优化

高分请问一下,新特新解读 | MySQL 8.0 对 count(*)的优化
最新回答
喻夜白

2026-10-23 13:58:45

MySQL 8.0 对 count(*) 的优化主要体现在取消 sql_calc_found_rows 语法、改进执行计划成本计算,并推荐直接使用 select count(*) 的标准写法,其性能相比 5.7 有显著提升。

一、MySQL 8.0 对 count(*) 的核心优化点
  1. 取消 sql_calc_found_rows 语法

    该语法在 MySQL 8.0 中被标记为废弃,未来版本将移除。其通过全表扫描隐式计算行数,性能较差且在主从架构中可能因语句级存储导致数据不一致。

    替代方案:直接使用 select count(*) 分两次查询(一次获取数据,一次获取总数),或通过其他优化手段(如索引覆盖)减少计算开销。

  2. 优化执行计划成本计算

    MySQL 8.0 对 count(*) 的执行计划成本(cost_info)进行了优化。以主键索引扫描为例,8.0 的 query_cost(309.95)比 5.7(622.40)降低约 50%,主要得益于:

    更精确的 I/O 成本估算:8.0 改进了索引扫描的 I/O 模型,减少了不必要的磁盘读取。

    优化器策略调整:8.0 更倾向于使用索引覆盖扫描(using_index=true),避免回表操作。

  3. 推荐标准写法

    MySQL 8.0.17 起建议直接使用 select count(*) from table_name,而非依赖触发器、近似值或特殊表结构。此写法在大多数场景下性能足够,且无需维护额外逻辑。

二、MySQL 8.0 优化前后的对比分析
  1. 执行计划差异

    MySQL 5.7:

    使用主键索引扫描时,query_cost 较高(622.40),且 eval_cost(614.40)占比较大,表明计算开销主要在行数据评估。

    MySQL 8.0:

    query_cost 降至 309.95,read_cost(2.75)显著降低,说明优化器更高效地利用了索引结构,减少了 I/O 和计算负担。

  2. 性能提升原因

    索引覆盖优化:8.0 在执行 count(*) 时更可能直接使用索引元数据(如主键索引的叶子节点数量),而非逐行读取数据。

    统计信息改进:8.0 的数据字典(Data Dictionary)提供了更准确的表大小统计,帮助优化器生成更优计划。

    并行查询支持(企业版):8.0 支持并行扫描大表,进一步加速 count(*) 执行。

三、替代方案的局限性
  1. 模拟计数器(触发器 + 额外表)

    缺点:需维护触发器逻辑,写性能下降;额外表需手动更新,可能存在数据不一致风险。

    适用场景:对实时性要求极高且能接受写性能损耗的极端场景。

  2. 近似值(information_schema.tables)

    缺点:数据非精确值,且统计信息可能延迟更新。

    适用场景:新闻展示等对精确性要求不高的分页场景。

  3. 特殊表结构(如连续主键)

    缺点:依赖表设计(如主键连续无间隙),通用性差。

    适用场景:主键设计符合条件的特定业务表。

四、最佳实践建议
  1. 直接使用 select count(*)

    适用于大多数场景,尤其是表数据量中等(百万级以下)时,8.0 的优化已足够高效。

  2. 利用索引覆盖

    为常用查询条件创建合适索引,确保 count(*) 执行时能利用 using_index 优化。

  3. 避免全表扫描

    确保查询有明确的索引条件,避免优化器回退到全表扫描计划。

  4. 监控执行计划

    使用 EXPLAIN FORMAT=JSON 分析 count(*) 查询,确认是否使用了优化后的计划(如低 query_cost 和 using_index=true)。

  5. 考虑缓存策略

    对高频查询的 count(*) 结果,可通过应用层缓存(如 Redis)减少数据库压力。