直方图未生效需先确认ANALYZE是否真正执行并覆盖目标列;直方图仅对参与查询且满足采样阈值的列生成,可通过pg_stats检查histogram_bounds是否非空。

直方图没生效?先检查 ANALYZE 是否真执行了
很多情况下你以为更新了统计信息,其实 ANALYZE 根本没跑成功,或者只跑了表没跑列。PostgreSQL 默认对所有列收集简单统计(行数、空值率、最常见值等),但直方图(histogram_bounds)只对参与查询条件的列、且满足采样阈值时才生成。
实操建议:
- 用
SELECT * FROM pg_stats WHERE tablename = 'your_table' AND attname = 'your_col';查看histogram_bounds字段是否为非空数组;为空说明直方图没建出来 - 显式指定列:运行
ANALYZE your_table (your_col);,比全表ANALYZE更快,也更容易命中直方图生成逻辑 - 若列基数极高(如 UUID、时间戳),默认采样可能不足,需加大
default_statistics_target(例如设为 1000),再ANALYZE - 注意:
VACUUM ANALYZE中的ANALYZE阶段不会自动提升统计精度,它仍受当前default_statistics_target约束
WHERE 条件匹配不上直方图区间?小心数据倾斜 + 谓词写法
直方图本质是把列值域切分成等频(不是等宽)桶,优化器靠它估算 WHERE col > X 会返回多少行。但如果查询值 X 落在某个桶内部,而该桶恰好聚集了大量倾斜数据(比如 90% 的订单集中在最近 1 小时),估算就会严重失真。
实操建议:
- 用
\d+ your_table或查pg_stats.histogram_bounds,手动看直方图分界点,对比你的查询常量是否卡在“高密度桶”边缘 - 避免写
col >= '2024-01-01'这类边界模糊谓词;改用col > '2023-12-31 23:59:59.999'让优化器更明确落在哪个桶 - 如果倾斜严重(如状态字段只有 'pending'/'done',但 99% 是 pending),直方图意义不大,应建部分索引:
CREATE INDEX idx_orders_pending ON orders (created_at) WHERE status = 'pending'; - 注意:范围查询(
BETWEEN、>=)比等值查询(=)更依赖直方图质量;IN列表过长时,优化器可能退化为选择率粗略估算
EXPLAIN 显示 rows=1000,实际扫描 100 万行?直方图只是估算依据之一
EXPLAIN 里 rows 是优化器基于直方图、MCV(most common values)、空值率等综合推算的结果,不是保证。当多表 JOIN + 多条件过滤叠加时,优化器会做独立性假设(比如认为 status 和 created_at 无关),一旦现实存在强相关性(如新订单几乎都是 pending),估算就崩了。
实操建议:
- 不要单看一个
rows值下结论;用EXPLAIN (ANALYZE, BUFFERS)对比actual rows和rows差距,确认是否真偏差大 - 检查是否触发了“行数低估 → 选错连接顺序 → 大表驱动小表”的恶性循环;此时强制改写 SQL 或加
JOIN LATERAL可能比调统计信息更有效 - PostgreSQL 12+ 支持扩展统计(
CREATE STATISTICS),对相关列组合建统计,比单列直方图更能缓解多条件误判,但需手动创建且不自动更新 - 注意:即使直方图完美,如果
work_mem不足导致哈希连接降级为归并或嵌套循环,性能问题也不在统计信息层面
MySQL / Oracle 用户别直接套用 PostgreSQL 直方图逻辑
各数据库直方图实现差异极大:PostgreSQL 用等频直方图 + MCV,MySQL 8.0+ 的 HISTOGRAM 默认是等深(SINGLE_PRECISION),Oracle 则分 HEIGHT BALANCED 和 FREQUENCY 两类,且对 NULL、绑定变量处理完全不同。
实操建议:
- MySQL:必须显式启用
ANALYZE TABLE t UPDATE HISTOGRAM ON c;,且默认只存 100 个桶;查看用SELECT * FROM information_schema.COLUMN_STATISTICS; - Oracle:直方图是否启用取决于
DBMS_STATS的method_opt参数,比如'FOR COLUMNS SIZE AUTO'才可能触发,不是 ANALYZE 就自动有 - 共性陷阱:所有数据库中,直方图对
LIKE '%xxx'、函数索引表达式(如UPPER(name))均无效,优化器只能回退到全表扫描估算 - 注意:SQL Server 的统计信息叫 “statistics object”,虽含直方图,但更新机制依赖自动采样比例和修改行数阈值,不主动
UPDATE STATISTICS很容易过期
直方图从来不是“开了就灵”的开关,它是优化器在信息不完备时做的概率游戏——你给的数据分布越真实,它猜得越准;但一旦业务逻辑天然倾斜,或者查询模式绕过统计覆盖范围,就得靠索引、分区、重写甚至物化结果来兜底。










