统计信息过期如何影响优化器判断——增长型大表的采集前后执行计划与参数实验实战
2026/8/24 14:50:12 网站建设 项目流程

文章目录

    • 每日一句正能量
    • 1. 背景与问题:优化器不是看真实数据做计划,而是看“统计摘要”做判断
    • 2. 环境与数据:为什么增长型大表最容易出现统计滞后
      • 2.1 为什么表越大,“20%变化”越可怕?
      • 2.2 表总行数增长只是第一层问题
      • 2.3 MCV为什么重要?
      • 2.4 n_distinct为什么会影响GROUP BY和JOIN?
      • 2.5 日期直方图特别容易在追加型表里过期
    • 3. 复现过程:ANALYZE前后,同一SQL从21秒降到3秒
      • 3.1 基线SQL
      • 3.2 采集前计划
      • 3.3 先确认统计是否陈旧
      • 3.4 单变量实验:只执行ANALYZE
      • 3.5 ANALYZE后计划
      • 3.6 为什么ANALYZE后计划会变?
    • 4. 方案实施:普通ANALYZE还不够时,怎么继续提高判断精度
      • 4.1 第一层:建立“统计新鲜度”指标
      • 4.2 第二层:超大表单独调整auto analyze阈值
      • 4.3 不要全库统一把scale factor调很低
      • 4.4 第三层:提高关键列statistics target
      • 4.5 为什么不建议全局statistics target直接1000?
      • 4.6 第四层:多列相关条件使用扩展统计
      • 4.7 创建扩展统计
      • 4.8 扩展统计解决什么问题?
      • 4.9 第五层:采集后做计划回归
      • 4.10 统计采集后的验证指标
      • 4.11 什么时候不要第一时间加Hint?
      • 4.12 统计信息不是越“实时”越好
      • 4.13 超大追加表要结合业务波次
      • 4.14 分区表应关注“新分区”统计
      • 4.15 统计信息采样存在天然误差
    • 5. 结果对比:统计采集本身就能让SQL从21.6秒降到3.4秒
        • E0:旧统计
        • E1:普通ANALYZE
        • E2:提高关键列statistics target
        • E3:扩展统计
      • 5.1 汇总
      • 5.2 优化收益不只是Execution Time
      • 5.3 统计信息还会影响Hash内存
      • 5.4 统计信息也会影响GROUP BY
      • 5.5 P99更能暴露热点估算错误
    • 6. 风险与复盘:统计信息采集不是“越多越好”,而是要让优化器获得足够准确的世界模型
      • 6.1 风险一:全库ANALYZE造成资源冲击
      • 6.2 风险二:statistics target过高
      • 6.3 风险三:只看last_analyze时间
      • 6.4 风险四:自动ANALYZE阈值太低
      • 6.5 风险五:扩展统计对象太多
      • 6.6 风险六:统计更新后计划变化
      • 6.7 风险七:出现回归就删除新统计
  • 推荐统计治理模型
  • 推荐诊断顺序
  • 回退方案
  • 最终复盘
  • 附录 A:最小统计采集
  • 附录 B:提高列统计目标
  • 附录 C:扩展统计
  • 附录 D:最低验收门禁

每日一句正能量

珍惜是柴米油盐里小心翼翼的呵护。
珍惜不是空话,是做饭时记得对方的口味,是疲惫时递上的一杯热水。浪漫的真谛,就藏在日复一日的琐碎与平淡中,被一双珍视的眼睛看见,被一双用心的手打捞起来。

主题:统计信息 / 增长型大表 / 优化器判断
重点ANALYZE、estimated rows、actual rows、MCV、直方图、n_distinctdefault_statistics_target、扩展统计、auto analyze、执行计划前后对比
适用场景:KingbaseES 中订单、流水、日志、交易明细、审计记录等持续快速增长的大表,尤其适用于“SQL 没变、索引没变,但某天开始突然变慢”的生产问题。


1. 背景与问题:优化器不是看真实数据做计划,而是看“统计摘要”做判断

数据库优化器在生成计划时,并不会真的先执行:

SELECT COUNT(*)

去确认每个条件会返回多少行。

它必须在 SQL 真正运行之前,根据:

表行数 页面数 NULL比例 n_distinct 最常见值 MCV 直方图 列相关性

估算:

这个条件会返回多少行? 这个 Join 两边有多大? 应该走索引还是全表扫描? 应该 Nested Loop 还是 Hash Join? Hash 要准备多少内存? Sort 会处理多少行?

KingbaseES 官方优化器统计信息文档明确说明,数据库采用基于成本的优化器 CBO,而统计信息就是代价估算的基础。统计信息是通过采样收集的数据概览,不是查询执行时的实时真相。

因此统计信息一旦过期,真正发生的事情不是:

数据库不知道表有多少行

这么简单。

而是:

优化器的整个成本模型开始建立在一个已经过时的数据世界里。

例如:

统计采集时: 表 1亿行 租户A占5% 两个月后: 表 3亿行 新增数据大量属于租户A 租户A实际占30% 统计仍然认为: 租户A≈5%

那么查询:

WHEREtenant_id='A'

优化器可能估:

150万

实际却:

9000万

这会直接影响:

Index Scan vs Bitmap/Seq Scan

以及:

Nested Loop vs Hash Join

的选择。

KingbaseES 官方 SQL 调优资料也明确指出:陈旧统计信息可能使优化器产生低效执行计划。

本文的核心观点是:

统计信息过期不是一个“维护动作没做”的小问题,而是优化器用于判断 Scan、Join、Aggregate、Sort 和内存成本的输入数据已经失真。


2. 环境与数据:为什么增长型大表最容易出现统计滞后

示例系统:

数据库: KingbaseES V9 表: growing_order 初始: 1亿行 两个月后: 3亿行 每日新增: 800万~1200万 热点租户: 过去5% 现在30%+ 热点状态: status=1 比例从20%增长到75% 最近日期: 新增数据高度集中

表结构:

CREATETABLEgrowing_order(order_idBIGINTPRIMARYKEY,tenant_idBIGINTNOTNULL,customer_idBIGINTNOTNULL,statusINTNOTNULL,biz_dateDATENOTNULL,amountNUMERIC(18,2));

索引:

CREATEINDEXidx_growing_order_tenant_dateONgrowing_order(tenant_id,biz_date);CREATEINDEXidx_growing_order_tenant_status_dateONgrowing_order(tenant_id,status,biz_date);

2.1 为什么表越大,“20%变化”越可怕?

KingbaseES 官方自动清理参数文档说明,自动 ANALYZE 的触发与:

autovacuum_analyze_threshold + autovacuum_analyze_scale_factor × 表规模

有关。

默认参数中:

autovacuum_analyze_threshold = 50 autovacuum_analyze_scale_factor = 0.2

意味着随着表越来越大:

按比例触发所需的变化行数

也越来越大。

如果表已经:

5亿行

20% 就是:

1亿行

即使业务每天新增:

1000万

统计信息也可能在一段时间内无法完全反映最新分布。

注意:

“新增1000万”

并不等于一定立刻触发一次你期望的统计刷新。

实际还受:

自动维护调度 数据库负载 表级设置 版本参数

影响。

因此增长型大表应该有:

自己的统计新鲜度策略

而不是全部依赖默认阈值。


2.2 表总行数增长只是第一层问题

更危险的是:

分布发生改变

例如过去:

tenant_id=999999 只有10万订单

某次业务迁入以后:

突然增加800万

表总行数只增加:

4%

看起来变化不大。

但这个租户的选择性已经从:

极高选择性

变成:

低选择性

这会直接改变:

索引扫描是否还划算

所以:

统计新鲜度不能只看“整表变了多少”,还要看关键业务值的分布是否发生跃迁。


2.3 MCV为什么重要?

假设:

tenant_id

存在大量普通租户:

每个只有1万~5万行

但一个超级租户:

800万

如果超级租户进入:

Most Common Values

优化器能对它使用更准确的频率。

如果采样没有捕捉到,或者旧统计里的频率已经过期:

优化器就可能用平均分布估算

这对热点参数非常危险。


2.4 n_distinct为什么会影响GROUP BY和JOIN?

n_distinct描述:

某列大约有多少不同值

它会影响:

GROUP BY组数 Join结果规模 选择率

例如:

customer_id

从:

1000万个distinct

增长到:

6000万个

但旧统计仍停留在:

1000万

优化器对:

HashAggregate Hash Join

内存和结果规模的估算都会受到影响。


2.5 日期直方图特别容易在追加型表里过期

订单、流水、日志这类表:

数据不断向时间轴右侧追加

旧统计采集时:

最大日期=2026-05-01

现在:

已经到2026-08-01

如果查询:

WHEREbiz_date>='2026-07-01'

统计信息没有及时更新,就可能很难准确判断:

最近一个月到底有多少数据

这就是典型:

Ascending Column / Append-only Distribution

问题。


3. 复现过程:ANALYZE前后,同一SQL从21秒降到3秒

3.1 基线SQL

SELECTo.order_id,o.customer_id,o.amountFROMgrowing_order oWHEREo.tenant_id=:tenant_idANDo.status=1ANDo.biz_date>=:start_dateANDo.biz_date<:end_date;

执行:

EXPLAIN(ANALYZE,BUFFERS,VERBOSE)SELECT...;

热点参数:

tenant_id=999999 status=1 最近90天

3.2 采集前计划

示例:

Index Scan estimated rows=18,000 actual rows=8,200,000

误差:

455倍

如果后面继续 Join:

customer

优化器可能认为:

只有1.8万行

于是选择:

Nested Loop

结果:

Inner loops 数百万

P95:

21.6s

Buffers Read:

980万

这时很多 DBA 第一反应:

索引不行 Join算法不行

但真正的第一个问题应该是:

为什么18000变成了820万?

3.3 先确认统计是否陈旧

需要记录:

上次ANALYZE时间 表当前大概行数 上次统计时行数 近期INSERT/UPDATE/DELETE 热点参数增长

不要只看:

SQL慢

就直接执行:

CREATE INDEX

3.4 单变量实验:只执行ANALYZE

这是整篇最重要的实验方法。

不要同时:

改索引 改SQL 调work_mem 改Join开关

只执行:

ANALYZEgrowing_order;

然后:

同一SQL 同一参数 同一测试环境

再次运行。


3.5 ANALYZE后计划

示例:

estimated rows=6,900,000 actual rows=8,200,000

估算误差从:

455倍

降到:

约1.19倍

计划:

Index Scan + Nested Loop

变成:

Bitmap Scan + Hash Join

P95:

21.6s →3.4s

Buffers:

980万 →190万

这里已经可以证明:

主要根因不是 SQL 突然写坏,而是旧统计让优化器严重低估热点参数。


3.6 为什么ANALYZE后计划会变?

因为优化器重新获得了:

表规模 热点频率 直方图 distinct NULL比例

等数据概览。

然后重新计算:

Index Scan成本 Bitmap Scan成本 Seq Scan成本 Join成本

原来:

820万行

在优化器世界里只有:

1.8万行

现在:

终于接近真实值

所以计划自然会改变。


4. 方案实施:普通ANALYZE还不够时,怎么继续提高判断精度

4.1 第一层:建立“统计新鲜度”指标

不能只监控:

CPU 连接数 锁

还要监控:

last_analyze last_autoanalyze 表增长量 修改行数 reltuples

对于:

关键大表

建议定义:

Freshness SLA

例如:

热点交易表: 统计延迟不超过6小时 历史归档表: 不超过7天

不同表不能一刀切。


4.2 第二层:超大表单独调整auto analyze阈值

示例:

ALTERTABLEgrowing_orderSET(autovacuum_analyze_threshold=50000,autovacuum_analyze_scale_factor=0.02);

这是:

方法示例

不是通用推荐值。

如果表:

5亿行

scale factor:

0.2

对应比例变化量非常大。

调整到:

0.02

可以更早触发。

但采集频率也会增加。

所以必须平衡:

统计新鲜度 vs ANALYZE资源开销

4.3 不要全库统一把scale factor调很低

如果:

10000张表

全部设置:

0.01

可能导致:

后台ANALYZE频繁执行 CPU/IO增加

正确方式:

按增长速度分层

例如:

G0: 静态维表 G1: 普通OLTP表 G2: 增长型大表 G3: 热点超大事实表

只对:

G2/G3

做特殊策略。


4.4 第三层:提高关键列statistics target

KingbaseES 官方 ANALYZE 文档说明:

default_statistics_target

控制默认统计信息量。

也可以:

ALTERTABLE...ALTERCOLUMN...SETSTATISTICS...

单独提高某列目标。

例如:

ALTERTABLEgrowing_orderALTERCOLUMNtenant_idSETSTATISTICS500;

然后:

ANALYZEgrowing_order;

目的:

更多MCV 更多直方图桶 更大采样

提高热点和长尾估算质量。


4.5 为什么不建议全局statistics target直接1000?

因为统计目标越大:

采样更多 ANALYZE更慢 sys_statistic更大 规划时间也可能略增

KingbaseES 官方文档明确指出,提高统计目标是在:

估算精度 ANALYZE时间 统计目录空间

之间做权衡。

所以应该:

只提高关键列

例如:

tenant_id biz_date status region_id

4.6 第四层:多列相关条件使用扩展统计

查询:

WHEREtenant_id=:tANDstatus=1ANDbiz_date>=:d

单列统计通常会把:

tenant_id status biz_date

选择率近似组合。

如果三个条件高度相关:

估算可能仍然错误

KingbaseES 官方优化器统计文档明确指出,单列统计无法表达多列之间的相关性,因此提供:

dependencies ndistinct MCV

扩展统计。


4.7 创建扩展统计

CREATESTATISTICSst_order_tenant_status_date(dependencies,ndistinct,mcv)ONtenant_id,status,biz_dateFROMgrowing_order;

注意:

CREATE STATISTICS

只是建立统计对象。

官方文档明确说明:

真正的数据采集还必须执行 ANALYZE。

所以接着:

ANALYZEgrowing_order;

4.8 扩展统计解决什么问题?

例如:

超级租户的status=1比例=95% 普通租户=20%

单独看:

tenant_id

和:

status

都无法完整表达:

两者的关联关系

扩展统计可以帮助优化器更好判断:

tenant + status

组合选择率。


4.9 第五层:采集后做计划回归

ANALYZE不是:

做完就结束

因为官方文档也提醒:

统计变化可能使规划器选择发生变化

大多数情况下是改善。

但对:

边界SQL

也可能从一个计划切到另一个计划。

所以统计采集要有:

计划回归

至少检查:

Top SQL 核心接口 长查询 批处理

4.10 统计采集后的验证指标

每条关键 SQL 保存:

Plan Hash / Plan Shape estimated rows actual rows Buffers P95/P99 CPU Temp

目标不是:

计划必须不变

而是:

新计划是否更符合真实数据

4.11 什么时候不要第一时间加Hint?

KingbaseES SQL 调优资料提到,当统计信息误差仍无法解决时,可以考虑使用 Hint 控制执行计划。

但顺序应该是:

统计新鲜度 ↓ statistics target ↓ 扩展统计 ↓ SQL/索引 ↓ 最后才考虑Hint

如果一开始:

强制Nested Loop 强制Index Scan

你只是把:

错误统计造成的症状

固定住。

数据再增长一次:

Hint很可能再次变成坏计划

4.12 统计信息不是越“实时”越好

每执行一批:

1000行INSERT

就 ANALYZE 一次:

没有必要

采样本身有成本。

合理目标应该是:

统计变化足以影响计划时 及时更新

而不是:

每秒和真实数据完全同步

4.13 超大追加表要结合业务波次

例如:

每天凌晨批量导入3000万

最适合:

导入完成 ↓ 建/维护索引 ↓ ANALYZE ↓ 报表放量

而不是等:

自动ANALYZE什么时候碰巧触发

这和迁移后大批量导入完成需要手工 ANALYZE 的工程原则是一致的。


4.14 分区表应关注“新分区”统计

增长型事实表经常:

按月/日分区

新分区:

刚创建 刚装载

统计信息可能为空或很弱。

应在:

批量加载完成

后明确执行:

ANALYZE新分区/相关对象

不要假设旧分区统计可以代表新分区。


4.15 统计信息采样存在天然误差

官方调优指南明确指出:

统计是采样得到的

因此即使:

刚刚ANALYZE

也不代表:

estimated=actual

完全一致。

我们真正关注的是:

误差是否足以改变计划

例如:

estimated=700万 actual=820万

通常可以接受。

而:

estimated=1.8万 actual=820万

就是灾难级偏差。


5. 结果对比:统计采集本身就能让SQL从21.6秒降到3.4秒

E0:旧统计
estimated: 18,000 actual: 8,200,000 Plan: Index Scan + Nested Loop Buffers: 980万 P95: 21.6s

E1:普通ANALYZE
estimated: 6,900,000 actual: 8,200,000 Plan: Bitmap Scan + Hash Join Buffers: 190万 P95: 3.4s

已经:

6倍+

改善。


E2:提高关键列statistics target
tenant_id: 500 biz_date: 500 status: 300

重新 ANALYZE。

示例:

estimated: 7,900,000 actual: 8,200,000 P95: 2.8s

热点值估算进一步改善。


E3:扩展统计

建立:

tenant_id status biz_date

相关性统计。

重新 ANALYZE。

示例:

estimated: 8,100,000 actual: 8,200,000 P95: 2.3s

Plan:

稳定


5.1 汇总

实验统计状态EstimatedActual计划P95
E0过期1.8万820万Index+Nested Loop21.6s
E1ANALYZE690万820万Bitmap+Hash3.4s
E2高统计目标790万820万Bitmap+Hash2.8s
E3扩展统计810万820万稳定Hash路径2.3s

以上均为方法演示数据,不是生产实测。


5.2 优化收益不只是Execution Time

Buffers:

980万 →150万

说明:

数据库真实读取工作量下降

Nested Loop:

数百万Inner loops

消失。

CPU:

下降

因此这是:

计划质量真正改善

而不是:

缓存刚好变热

5.3 统计信息还会影响Hash内存

如果优化器估:

Hash Build=1万行

实际:

500万

那么:

内存预算 Batches 临时文件

都会比预想糟糕。

所以统计过期也会间接制造:

Hash Join落盘

问题。

这和前面的 Hash Join 文章并不是两个独立主题。

根因链可能是:

统计过期 →低估Build →选Hash →内存不足 →多Batch →Temp爆炸

5.4 统计信息也会影响GROUP BY

estimated groups:

1000

actual groups:

100万

那么:

HashAggregate

需要维护的状态量完全不同。

所以统计治理应该覆盖:

Scan Join Aggregate Sort

而不是只看索引扫描。


5.5 P99更能暴露热点估算错误

普通参数:

都估得准

热点参数:

估错几百倍

总体平均:

可能看起来不错

真正出问题的是:

少量VIP/大租户

所以要按:

参数组

看 P95/P99。


6. 风险与复盘:统计信息采集不是“越多越好”,而是要让优化器获得足够准确的世界模型

6.1 风险一:全库ANALYZE造成资源冲击

大型数据库:

几TB 上万张表

高峰期直接:

ANALYZE;

可能带来明显 IO/CPU。

应该:

按关键表 按波次 按维护窗口

执行。


6.2 风险二:statistics target过高

全局:

1000

会增加:

采样 ANALYZE时间 统计目录 规划成本

优先:

列级提高

6.3 风险三:只看last_analyze时间

昨天刚 ANALYZE:

不代表今天就一定准确

如果昨晚:

导入5000万热点数据

统计已经再次失真。

所以应该同时看:

时间 + 变化量

6.4 风险四:自动ANALYZE阈值太低

scale factor:

太低

会让超高频写表频繁 ANALYZE。

资源成本可能超过收益。

所以阈值要根据:

增长速度 表大小 查询重要性

设计。


6.5 风险五:扩展统计对象太多

所有列组合都建立:

不现实

官方文档也指出,列组合数量可能非常庞大,因此扩展统计需要:

人工针对关键相关条件创建

只服务:

真正影响计划的列组合

6.6 风险六:统计更新后计划变化

ANALYZE 后:

计划改变

不是异常。

但需要:

Top SQL回归

因为某些临界 SQL 可能改变 Join 顺序或访问路径。


6.7 风险七:出现回归就删除新统计

这也是错误做法。

新统计通常:

更接近真实数据

如果某 SQL 因此变差,应先检查:

成本模型 索引 SQL结构 参数敏感

而不是第一时间:

恢复错误世界模型

推荐统计治理模型

对增长型大表建立:

表规模 + DML增长 + 最近ANALYZE + 关键列分布 + Top SQL估算误差

五维监控。

可以定义:

S0: 静态/低变化 S1: 普通业务表 S2: 高增长大表 S3: 高增长+高倾斜核心表

S3:

更低auto analyze比例 关键列高statistics target 扩展统计 采集后计划回归

这比:

全库统一参数

更合理。


推荐诊断顺序

1. 保存慢SQL计划 2. 对比estimated/actual 3. 看表增长和最近ANALYZE 4. 检查热点参数/日期分布 5. 单变量执行ANALYZE 6. 对比计划和P95 7. 必要时提高列statistics target 8. 多列相关则CREATE STATISTICS 9. 再次ANALYZE 10. 固化auto analyze表级策略

回退方案

统计治理的回退和普通 SQL 回退不完全一样。

如果调整:

statistics target auto analyze参数 扩展统计

后出现回归:

1. 保存新旧计划和参数 2. 不要立即清除所有新统计 3. 若列statistics target过高,恢复原值后重新ANALYZE 4. 若表级autovacuum参数不合适,恢复原storage parameter 5. 若扩展统计被证明有问题,记录证据后DROP并重新ANALYZE 6. 重测普通/热点/长尾参数

最重要的是:

统计回退也必须保持单变量,不能同时改SQL、索引、Hint。

否则无法知道真正原因。


最终复盘

统计信息本质上是:

优化器对数据世界的压缩模型

它不需要:

100%精确

但必须:

足够准确到不会选错成本数量级

对于增长型大表,最危险的不是:

表从1亿变3亿

本身。

而是:

热点分布 日期分布 distinct 列相关性

都已经变化,优化器仍然使用过去的概率模型。

如果只记住一句话:

统计信息过期真正破坏的不是“行数显示”,而是优化器判断索引、扫描、Join、Hash、Sort 和 Aggregate 的整个成本基础;当 estimated 与 actual 跨越几个数量级时,应该先修正统计世界模型,再去改 SQL 和索引。

这也是为什么生产调优时:

EXPLAIN ANALYZE

里的:

estimated rows vs actual rows

永远是最值得先看的数据之一。


附录 A:最小统计采集

ANALYZEgrowing_order;

附录 B:提高列统计目标

ALTERTABLEgrowing_orderALTERCOLUMNtenant_idSETSTATISTICS500;ANALYZEgrowing_order;

附录 C:扩展统计

CREATESTATISTICSst_order_tenant_status_date(dependencies,ndistinct,mcv)ONtenant_id,status,biz_dateFROMgrowing_order;ANALYZEgrowing_order;

附录 D:最低验收门禁

[ ] 上次ANALYZE时间已记录 [ ] 表增长量已记录 [ ] 热点参数已覆盖 [ ] estimated/actual已比较 [ ] ANALYZE单变量实验已执行 [ ] statistics target有依据 [ ] 扩展统计仅用于关键相关列 [ ] 采集后Top SQL已回归 [ ] P95/P99达到SLA [ ] Buffer/CPU无异常回归 [ ] 自动采集阈值已文档化 [ ] 回退参数已保存

转载自:https://blog.csdn.net/u014727709/article/details/163950186
欢迎 👍点赞✍评论⭐收藏,欢迎指正

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询