执行计划进阶:读懂统计信息与基数估算,理解优化器的“思考方式”

简介: 执行计划是SQL优化的核心工具,但很多人只关注type和Extra,忽略了执行计划背后的决策依据——统计信息与基数估算。本文从优化器的决策逻辑出发,解释统计信息如何影响基数估算、基数估算如何决定执行计划的选择。通过真实案例展示统计信息过旧如何导致优化器“选错路”,以及如何通过更新统计信息、使用扩展统计等方法来纠正。帮助读者从“看懂执行计划”进阶到“理解优化器为什么这么选”。

大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!

上周我们讲了怎么看执行计划——type、key_len、rows、filtered、Extra,每个字段都拆开讲了。但有一个问题一直没回答:优化器是根据什么来决定走哪个执行计划的?

你肯定遇到过这种情况:明明有合适的索引,优化器却选了全表扫描;明明A计划更快,优化器却选了B计划。这时候你可能会骂优化器“笨”,但其实它只是做了一个“基于现有信息的最优判断”——而它依赖的“现有信息”,就是​统计信息​。

如果统计信息不准,优化器的判断就会出错,最终执行计划就会跑偏。

今天我们把执行计划再往深挖一层,讲清楚统计信息和基数估算到底是怎么回事,以及当执行计划“跑偏”时,怎么从统计信息入手把它拉回来。

一、优化器的决策逻辑:它不是“猜”,而是“算”

数据库优化器是基于代价(Cost-Based) 的。它不会凭感觉选执行计划,而是会计算每种可能的执行方式的“代价”,然后选代价最小的那个。

这个“代价”是怎么算出来的?核心公式可以简化为:

代价 ≈ 扫描行数 × 单行处理成本 + 其他开销

其中“扫描行数”就是基数估算(Cardinality Estimation) ——优化器估算的、某个操作会返回的行数。

而基数估算依赖的数据,就是​统计信息​——表有多少行、每列有多少个不同值、数据分布如何。

二、统计信息:优化器的“眼睛”

统计信息是优化器做出决策的依据。如果统计信息不准,优化器的判断就会跟着错。

统计信息主要包括以下几类:

统计信息类型 作用 影响
表级行数 估算全表扫描的基数 决定是否走全表扫描
列级不同值数量(NDV) 估算过滤条件的选择率 决定索引是否被选中
列级NULL值比例 影响条件判断的准确性 影响WHERE条件的估算
数据分布(直方图) 了解列值的分布情况 影响范围查询的估算

举个例子:一张订单表有1000万行,status列有3个不同值(PAID、UNPAID、REFUND)。如果统计信息准确,优化器知道status='PAID'大约占30%,也就是300万行。如果统计信息过旧(比如只统计到表有100万行),优化器会估算status='PAID'只有30万行,从而可能错误地选择索引扫描而非全表扫描——或者反过来。

三、基数估算:优化器的“算力”

有了统计信息,优化器就开始做基数估算——预测每个操作会返回多少行。

基数估算的核心是选择率(Selectivity) 计算。选择率表示某个过滤条件能筛选掉多少数据。

选择率越高,过滤掉的数据越多,返回的行数越少;选择率越低,返回的行数越多。

对于等值条件(WHERE status = 'PAID'),选择率 ≈ 1 / 该列的不同值数量。如果status有3个不同值,选择率约33%。

对于范围条件(WHERE create_time > '2026-01-01'),选择率依赖直方图统计信息来估算数据分布。

如果统计信息不准确,选择率就会算错,基数估算就会偏差,最终执行计划就可能选错。

四、真实案例:统计信息过旧导致的执行计划跑偏

我遇到过这样一个案例:一张日志表有500万行,user_id列上有索引。业务方反馈一条查询突然变慢:

SELECT * FROM user_logs WHERE user_id = 12345 AND create_time > '2026-01-01';

正常情况下,这个查询应该走(user_id, create_time)复合索引,扫描几十行就返回。但实际执行计划显示type=ALL,全表扫描。

查看统计信息后发现,这张表的统计信息还是三个月前收集的——当时表只有50万行。优化器根据旧统计信息估算:user_id = 12345可能返回很多行(因为旧数据中该用户有大量记录),全表扫描可能更“划算”。

执行ANALYZE TABLE user_logs更新统计信息后,优化器重新估算,发现user_id = 12345在500万行中只有几十条记录,复合索引被正确选中,查询从5秒降到了0.05秒。

五、如何检查统计信息是否准确

方法一:对比EXPLAIN的rows与实际行数

执行EXPLAIN后,看rows列的估算值。然后实际执行查询(或使用EXPLAIN ANALYZE),对比实际扫描行数。如果估算值和实际值差了一个数量级以上,统计信息很可能过旧了。

方法二:查看统计信息的最后更新时间

-- MySQL
SELECT table_name, update_time FROM information_schema.tables WHERE table_name = 'your_table';

如果统计信息已经几周甚至几个月没有更新,而表的数据变化很大,就需要执行ANALYZE TABLE

方法三:查看索引的Cardinality

SHOW INDEX FROM your_table;

Cardinality列显示索引列的不同值数量估算。如果Cardinality与实际明显不符,说明统计信息需要更新。

六、如何维护统计信息

1. 定期执行ANALYZE TABLE

对于数据变化频繁的表,建议设置定时任务每天或每周执行ANALYZE TABLE。MySQL 8.0的统计信息虽然是持久化的,但自动更新阈值默认是表数据变化10%,对于核心业务表,这个阈值可能偏大。

2. MySQL 8.0的直方图支持

MySQL 8.0支持为列创建直方图统计信息,对于数据分布不均匀的列特别有用:

ANALYZE TABLE your_table UPDATE HISTOGRAM ON column_name WITH 100 BUCKETS;

3. 关注统计信息的更新时机

建议在以下操作后执行ANALYZE TABLE

  • 批量数据导入后
  • 大量数据删除后
  • 表结构变更后(如添加索引)
  • 定期维护窗口(如每周日凌晨)

七、进阶:当统计信息准确但执行计划仍然跑偏时

有时候统计信息是准确的,但优化器仍然选择了次优计划。这种情况可能的原因包括:

  • 参数设置问题​:某些数据库参数(如eq_range_index_dive_limit)影响优化器的行为
  • 优化器提示(Hint) :在极端情况下,可以使用USE INDEXFORCE INDEX引导优化器

但在使用Hint之前,建议先用EXPLAIN ANALYZE确认真实执行情况,再判断是否需要强制干预。

执行计划是优化器的“决策结果”,统计信息是优化器的“决策依据”,基数估算是优化器的“决策算法”。这三者形成一个完整的决策链条:​统计信息 → 基数估算 → 执行计划​。

如果执行计划看起来不合理,不要急着骂优化器“笨”。先检查统计信息是否准确——大概率是优化器“看错了”,而不是“算错了”。学会从这个角度理解执行计划,你就能从“看懂EXPLAIN”升级到“理解优化器为什么这么选”,在SQL优化上真正做到“知其然也知其所以然”。

小耶在手,SQL 不愁

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽……我们下次见~

相关文章
|
1月前
|
存储 人工智能 关系型数据库
湖库一体:2026年数据库架构的“终极答案”还是新瓶装旧酒?
2026年6月,OceanBase发布湖库一体AI数据库,阿里云PolarDB年初已推出AI数据湖库(Lakebase),Databricks也在6月推出了LTAP架构。“湖库一体”成为2026年数据库圈最热的概念之一。本文从湖库一体的概念定义出发,拆解其技术原理,对比“湖仓一体”与“湖库一体”的差异,分析三大厂商的落地路径,并讨论这一趋势对DBA和架构师的现实意义。
|
2月前
|
人工智能 Cloud Native 关系型数据库
MySQL 8.4 LTS来了!从8.0到8.4,DBA必须知道的5个核心变化
MySQL 8.0社区版将于2026年结束生命周期,8.4 LTS作为首个长期支持版本,提供5年超长支持周期(至2031年)。本文从InnoDB并行查询、Redo Log动态容量、默认认证插件变更、参数默认值调整、云原生适配五个维度,梳理DBA升级前必须掌握的核心变化,并提供升级检查清单。
|
2月前
|
SQL 运维 自然语言处理
国产向量数据库有哪些?两大技术流派深度对比与选型指南
向量数据库是2026年数据库领域增长最快的细分赛道之一。本文从RAG应用和企业知识库的实际需求出发,系统梳理国产向量数据库的两大技术流派——独立向量数据库与融合型向量数据库,深入对比两者的架构差异、适用边界和选型逻辑。
|
2月前
|
SQL 人工智能 自然语言处理
Vibe Coding 是什么?当“感觉编程”遇上数据库
Vibe Coding是2026年编程圈最火的概念之一,指开发者通过自然语言描述“感觉”或“意图”,由AI自动生成代码、调试、优化。本文从Vibe Coding的起源讲起,分析它如何改变数据库开发方式:从手写SQL到自然语言查询、从人工调索引到AI推荐、从经验运维到智能诊断。探讨这项趋势对DBA职业的影响,并给出拥抱变化的实用建议。技术会变,但人的判断力、审美和业务理解才是长期竞争力。
|
2月前
|
运维 监控 负载均衡
分布式集群架构选型:数据库场景下的分库分表与多副本方案
拆解分布式、集群、分布式集群三个概念的区别与联系,结合数据库场景讲清楚分片策略、一致性、负载均衡、故障转移等关键技术点,附架构对比表和避坑清单。
|
2月前
|
存储 SQL 关系型数据库
索引优化深潜(下):索引合并、ICP 与索引设计的实战法则
索引优化不止于单索引设计。本文深入讲解MySQL 5.6/8.0的高级索引特性:索引合并(Index Merge)的三种策略(交集、并集、排序并集)、索引条件下推(ICP)的工作原理和适用场景,以及如何使用MRR(多范围读取)优化随机I/O。通过真实案例演示如何利用这些特性提升查询性能,同时揭示索引合并可能带来的隐患。最后总结索引设计实战法则,帮助你从“能用索引”进阶到“用好索引”。
|
28天前
|
存储 传感器 监控
时序数据是什么?2026年企业为什么离不开时序数据库
时序数据是2026年增长最快的数据类型之一。据行业预测,工业物联网产生的时序数据量将占企业总数据量的75%以上,年复合增长率超过40%。时序数据已从“技术补充”升级为“核心资产”。本文从时序数据的基本概念出发,讲解时序数据的特征、应用场景,以及为什么传统数据库处理不了时序数据,帮助读者建立对时序数据的完整认知。
|
2月前
|
SQL 关系型数据库 MySQL
事务隔离级别选错了,数据可能被“吞”掉——从脏读到幻读,一次讲透
事务隔离级别是数据库并发控制的核心机制,但很多开发者和DBA对脏读、不可重复读、幻读的区别一知半解,遇到问题只能“加锁试试”。本文从四个隔离级别出发,用真实SQL案例讲透三种并发问题的本质差异,对比MySQL与PostgreSQL在默认隔离级别上的不同选择,并结合业务场景给出选型建议,帮助读者写出更可靠的事务代码。
|
1月前
|
SQL 存储 运维
SQL Server迁移必看!深度解析SQLServer兼容性三大核心维度与选型指南
SQL Server迁移是国产化替代中最复杂的场景之一。所谓SQLServer兼容性,通俗来说,就是让国产数据库能够“听懂”并“执行”原本运行在微软SQL Server上的指令,同时保持数据不丢失、业务不中断。本文从语法兼容、语义兼容、生态兼容三大维度深度解析SQLServer兼容性的本质,梳理T-SQL差异、存储过程转换、工具链适配等核心挑战,并提供系统化的迁移评估路径和选型建议,帮助读者在迁移启动之前就建立起清晰的认知图谱。
|
1月前
|
存储 SQL 关系型数据库
执行计划的“黑话”你听懂了吗?Extra列里藏着的8个性能信号
EXPLAIN是SQL优化的核心工具,但很多人只看type和key,忽略了Extra列——它才是执行计划里信息密度最高的部分。Using index和Using index condition有什么区别?Using temporary和Using filesort同时出现意味着什么?本文逐一拆解Extra列中8个最常见的性能信号,帮助读者从“看EXPLAIN”升级到“读懂EXPLAIN”。

热门文章

最新文章