default_statistics_target设为100是安全起点,关键列可单独设500~1000;需通过pg_stats验证采样精度,并用explain(analyze)确认估算改善。

default_statistics_target 设为多少才够用
PostgreSQL 的 default_statistics_target 控制 ANALYZE 收集的列统计信息精度,默认是 100。这个值不是越大越好,也不是越小越省事——它直接影响查询计划质量与 ANALYZE 开销的平衡点。
多数业务表设为 100 是安全起点;但遇到明显走错索引、JOIN 顺序异常、或 EXPLAIN 显示行数预估偏差超 10 倍时,就得调高。实测中,高频 WHERE 条件列、JOIN 列、ORDER BY 列,设到 500~1000 能显著改善计划稳定性。
-
100:默认值,适合结构简单、数据分布均匀的表 -
500:推荐起点,对含倾斜值(如 status=‘active’ 占 95%)、多范围条件的列更可靠 -
1000:仅建议在关键大表(>10M 行)且存在严重估算偏差时使用;ANALYZE 时间可能翻倍,pg_statistic 表体积也增大 - 超过
1000很少带来收益,反而让ANALYZE变成维护瓶颈
只给特定列单独调高统计精度
全局改 default_statistics_target 是粗暴方案,容易拖慢所有表的 ANALYZE。更合理的是按需精准增强——比如某个 user_id 列常被用于 JOIN,但值分布极不均匀,就该单独加权。
用 ALTER TABLE ... ALTER COLUMN ... SET STATISTICS 给单列指定更高目标值,优先级高于全局配置:
ALTER TABLE orders ALTER COLUMN customer_id SET STATISTICS 500;
- 该设置只影响该列,不影响其他字段或表
- 修改后需手动执行
ANALYZE orders(customer_id)才生效 - 若列名带特殊字符或大小写,记得用双引号:
ALTER TABLE t ALTER COLUMN "MyColumn" SET STATISTICS 500 - 注意:不能对表达式索引列或生成列直接设 STATISTICS
调完之后怎么验证有没有起效
改了配置不等于计划就变好了。得看两件事:统计信息是否真更新了,以及优化器是否用了新信息。
先查 pg_stats 确认采样精度提升:
SELECT tablename, attname, n_distinct, most_common_vals FROM pg_stats WHERE tablename = 'orders' AND attname = 'status';
- 对比
most_common_vals和most_common_freqs长度,变长说明采样更细了 - 若
n_distinct仍是 -1(表示“大于行数”),说明即使 target=1000,ANALYZE 仍没识别出实际去重数,可能需要检查数据是否真有大量重复 - 再跑
EXPLAIN (ANALYZE, BUFFERS),重点看Rows Removed by Filter比例是否下降,以及实际行数 vs 估算行数比值是否收敛到 2~5 倍内
容易被忽略的副作用和坑
调高 default_statistics_target 最直接的代价是 ANALYZE 更慢、更占内存,但还有几个隐蔽问题常被漏掉:
- ANALYZE 过程会持有
ShareUpdateExclusiveLock,在大表上可能阻塞 VACUUM 或其他 ANALYZE,尤其在低配实例上容易触发锁等待超时 - pg_statistic 表本身会膨胀——每个统计项存多个直方图桶和 MCV 列表,target 从 100→500,单列存储体积可能增加 3~4 倍
- 某些旧版本 PostgreSQL(如 9.6 之前)对 >1000 的 target 支持不稳,可能出现 ANALYZE 卡住或崩溃,务必在测试库验证
- 如果表启用了
autovacuum_analyze_scale_factor = 0但没配autovacuum_analyze_threshold,改了 target 后 ANALYZE 可能压根不自动触发
真正难的不是设个数字,而是判断哪一列的统计失真正在拖垮你的关键查询——这得靠 EXPLAIN 里反复比对估算和实际,而不是凭感觉调大。










