2026-10-23 13:58:45
MySQL 8.0 对 count(*) 的优化主要体现在取消 sql_calc_found_rows 语法、改进执行计划成本计算,并推荐直接使用 select count(*) 的标准写法,其性能相比 5.7 有显著提升。
一、MySQL 8.0 对 count(*) 的核心优化点取消 sql_calc_found_rows 语法
该语法在 MySQL 8.0 中被标记为废弃,未来版本将移除。其通过全表扫描隐式计算行数,性能较差且在主从架构中可能因语句级存储导致数据不一致。
替代方案:直接使用 select count(*) 分两次查询(一次获取数据,一次获取总数),或通过其他优化手段(如索引覆盖)减少计算开销。
优化执行计划成本计算
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),避免回表操作。
推荐标准写法
MySQL 8.0.17 起建议直接使用 select count(*) from table_name,而非依赖触发器、近似值或特殊表结构。此写法在大多数场景下性能足够,且无需维护额外逻辑。
执行计划差异
MySQL 5.7:
使用主键索引扫描时,query_cost 较高(622.40),且 eval_cost(614.40)占比较大,表明计算开销主要在行数据评估。
MySQL 8.0:
query_cost 降至 309.95,read_cost(2.75)显著降低,说明优化器更高效地利用了索引结构,减少了 I/O 和计算负担。
性能提升原因
索引覆盖优化:8.0 在执行 count(*) 时更可能直接使用索引元数据(如主键索引的叶子节点数量),而非逐行读取数据。
统计信息改进:8.0 的数据字典(Data Dictionary)提供了更准确的表大小统计,帮助优化器生成更优计划。
并行查询支持(企业版):8.0 支持并行扫描大表,进一步加速 count(*) 执行。
模拟计数器(触发器 + 额外表)
缺点:需维护触发器逻辑,写性能下降;额外表需手动更新,可能存在数据不一致风险。
适用场景:对实时性要求极高且能接受写性能损耗的极端场景。
近似值(information_schema.tables)
缺点:数据非精确值,且统计信息可能延迟更新。
适用场景:新闻展示等对精确性要求不高的分页场景。
特殊表结构(如连续主键)
缺点:依赖表设计(如主键连续无间隙),通用性差。
适用场景:主键设计符合条件的特定业务表。
直接使用 select count(*)
适用于大多数场景,尤其是表数据量中等(百万级以下)时,8.0 的优化已足够高效。
利用索引覆盖
为常用查询条件创建合适索引,确保 count(*) 执行时能利用 using_index 优化。
避免全表扫描
确保查询有明确的索引条件,避免优化器回退到全表扫描计划。
监控执行计划
使用 EXPLAIN FORMAT=JSON 分析 count(*) 查询,确认是否使用了优化后的计划(如低 query_cost 和 using_index=true)。
考虑缓存策略
对高频查询的 count(*) 结果,可通过应用层缓存(如 Redis)减少数据库压力。