3. 统计信息与基数估计:统计信息的作用、直方图、采样技术、基数估计误差
优化器做决策,靠的是什么?说白了,就是“猜”。但它不是瞎猜,它手里有一份地图——统计信息。没有统计信息,优化器就像蒙着眼开车,全凭运气。
我见过太多慢查询,根因就是统计信息过期或者压根没收集。你想想看,优化器连表里有多少行数据都不知道,它怎么选索引?怎么决定 Join 顺序?
3.1 统计信息到底存了什么?
统计信息不是玄学,它是一组实实在在的元数据。数据库在表、索引、列上维护这些数据,用来描述数据的分布特征。
| 统计项 | 说明 | 影响 |
|---|---|---|
| 行数(Cardinality) | 表或索引中记录的总数 | 决定全表扫描还是索引扫描 |
| 列的唯一值数(NDV) | 某列不同值的个数 | 影响过滤条件的选择性 |
| 列的 NULL 值数 | 空值数量 | 影响 IS NULL 条件的估算 |
| 数据分布(直方图) | 值的频率分布 | 影响范围查询、等值查询 |
| 聚簇因子 | 数据在磁盘上的物理顺序与索引顺序的接近程度 | 影响索引回表的代价 |
3.2 直方图——统计信息的“高清镜头”
默认情况下,优化器假设数据是均匀分布的。但现实中的数据,哪有那么规整?比如订单状态,90% 是“已完成”,5% 是“待支付”,5% 是“已取消”。如果没有直方图,优化器会以为每种状态各占三分之一,那估算出来的行数就完全跑偏了。
直方图的作用,就是告诉优化器:别天真了,数据分布长这样。
3.2.1 两种主流直方图
- 频率直方图(Frequency Histogram):每个桶对应一个值,桶的高度就是该值的出现次数。适合唯一值少的列。
- 等高直方图(Height-Balanced Histogram):每个桶包含大致相同数量的行,桶的边界是值。适合唯一值多的列。
我记得有一次排查一个 SQL,查询某天的订单,优化器选了全表扫描。我一看统计信息,直方图没开。加上直方图后,优化器发现那天的数据只占全表的 0.1%,果断走了索引。性能从 5 秒降到了 10 毫秒。
3.3 采样技术——全量统计太贵了
全表扫描收集统计信息,对于大表来说代价太高。所以数据库用了采样。采样就是在表里随机抽一部分数据,用这部分数据来推断全貌。
常见的采样方式有三种:
- 随机采样:随机抽取 N 行或 N% 的数据。简单,但可能漏掉小值。
- 系统采样:按数据块或页来采样。效率高,但如果数据分布不均匀,偏差会大。
- 自适应采样:先小比例采样,如果发现数据分布复杂,自动提高采样比例。这是现代数据库的趋势。
我曾经遇到一个坑:某张表有 1 亿行,DBA 为了省时间,把采样比例设成了 0.1%。结果优化器估算出来的行数偏差了 100 倍,导致执行计划选错了索引。后来我把采样比例调到 5%,问题就解决了。
3.4 基数估计误差——优化器也会“看走眼”
基数估计(Cardinality Estimation)是优化器根据统计信息,估算某个操作会返回多少行。这个估算值直接影响 Join 方式、索引选择、内存分配。
但估算嘛,总会有误差。误差的来源主要有:
- 统计信息过期:数据变了,统计信息没更新。
- 多列关联:优化器假设各列独立,但实际列之间有关联。比如“城市”和“邮编”,明显不是独立的。
- 复杂谓词:比如
WHERE a LIKE '%abc%',优化器很难精确估算。 - 采样偏差:采样数据不能代表整体分布。
误差大了会怎样?我见过一个案例:一个三表 Join 的查询,优化器估算中间结果只有 100 行,实际有 100 万行。结果它选了 Nested Loop Join,跑了 2 小时。改成 Hash Join 后,30 秒就出结果了。
3.5 知识体系:统计信息与基数估计全景图
下面这张图,把统计信息、直方图、采样、基数估计的关系串起来了。你可以把它当作本章的思维导图。
3.6 如何减少基数估计误差?
误差不可避免,但可以控制。我总结了几个实用方法:
- 及时更新统计信息:设置自动维护窗口,或者在数据变更超过 10% 后手动刷新。
- 使用多列统计信息:对于经常一起出现在 WHERE 条件中的列,创建多列统计信息,让优化器知道列之间的关联。
- 启用自适应查询优化:现代数据库(如 SQL Server 2017+、Oracle 12c+)支持运行时反馈,第一次执行错了,第二次会自动调整。
- 手动提示(Hint):当优化器实在猜不准时,用
OPTION (HASH JOIN)或/*+ USE_HASH */强制指定执行计划。
DBMS_STATS(Oracle)或 UPDATE STATISTICS(SQL Server)重新收集统计信息,往往立竿见影。
统计信息和基数估计,是优化器决策的基石。你喂给它什么样的统计信息,它就做出什么样的执行计划。所以,别让优化器“盲猜”——把统计信息维护好,它才能帮你选出最快的路。