首页/新闻资讯/正文详情

PostgreSQL 建了索引仍然全表扫描:1000 万行表 ANALYZE 前后的执行计划对比

发布时间:2026/11/5 7:56:53 来源:云帆数科 栏目:资讯中心
PostgreSQL 建了索引仍然全表扫描:1000 万行表 ANALYZE 前后的执行计划对比
本文摘要1000 万行表建好索引查询仍走全表扫描先怀疑索引往往查不到根因。用 EXPLAIN 对比 ANALYZE 前后执行计划可看到默认选择率 0.005 如何让优化器放弃索引。一、问题与结论一张orders表 1000 万行idx_orders_customer_id已建好且pg_index.indisvalid true查询条件正好命中该列EXPLAINSELECT*FROMordersWHEREcustomer_id500000;-- 输出Seq Scan on orders (cost0.00..274432.00 rows50000 width...)索引列顺序、函数包裹、隐式类型转换都排查过了全部正常。结论是索引没问题缺的是列统计信息。该表从未被ANALYZE过优化器对等值条件只能使用默认选择率DEFAULT_EQ_SEL 0.005估算 50,000 行匹配索引随机 I/O 代价高于顺序扫描。执行ANALYZE orders后估算降到约 10 行计划翻转为Index Scan。二、排查与选择依据三条查询即可判定不必再在索引上耗时间SELECTrelname,reltuples::BIGINTASest_rowsFROMpg_classWHERErelnameorders;SELECTattname,n_distinctFROMpg_statsWHEREtablenameordersANDattnamecustomer_id;SELECTn_mod_since_analyze,last_analyze,last_autoanalyzeFROMpg_stat_user_tablesWHERErelidorders::regclass;reltuples为-1PostgreSQL 14 起13 及更早版本可能显示 0该表从未经 ANALYZE/VACUUM。pg_stats查不到customer_id列级统计缺失。n_mod_since_analyze持续增长统计正在变旧。三条同时成立即可判定为统计信息问题。若pg_stats有记录且n_distinct合理则应回到索引本身继续排查。替代方案与取舍方案选择条件代价边界手动ANALYZE orders统计缺失或过期需立即修复一次性全表采样 I/O 与 CPU不解决之后再次变旧按表调低autovacuum_analyze_scale_factor变更频繁且查询关键的大表auto-analyze 运行更频繁写入极低的表纯属浪费ETL 末尾触发ANALYZE批次变更、变更量低于自动阈值依赖作业编排纪律漏配一次即失效不该用本文方案的情况WHERE status completed匹配约 99% 行ANALYZE 后优化器仍会正确地选择Seq Scan。这不是 ANALYZE 没生效而是代价计算本来就该选顺序扫描。三、关键原理阈值机制。auto-analyze 的触发条件是nchanges autovacuum_analyze_threshold autovacuum_analyze_scale_factor × reltuples默认值 50 与 0.1。代入 1000 万行50 0.1 × 10,000,000 1,000,050必须修改超过 100 万行才触发。10 万行的表改 1 万行就能触发1 亿行的表要改 1000 万行——表越大自动分析的相对响应能力越弱。选择率与代价翻转。ANALYZE 收集n_distinct、most_common_vals、histogram_bounds等列级统计场景等值选择率估算匹配行数无统计默认值0.00550,000 行ANALYZE 后1/100 万0.000001约 10 行估算 50,000 行时索引扫描需要约 50,000 次随机 I/O按random_page_cost 4.0计算总代价超过按seq_page_cost 1.0顺序读全表的 Seq Scan估算 10 行时索引扫描只需约 20 次随机 I/O代价远低于全表扫描。估算行数相差 5000 倍直接导致代价计算翻转。四、可运行示例环境PostgreSQL 1412–16 行为一致空库插入 1000 万行约 30–60 秒取决于硬件。-- 建表并关闭 autovacuum模拟 auto-analyze 尚未运行CREATETABLEorders(idBIGINTGENERATED ALWAYSASIDENTITY,customer_idINTNOTNULL,statusVARCHAR(20)NOTNULL,amountNUMERIC(12,2)NOTNULL,created_atTIMESTAMPNOTNULLDEFAULTNOW())WITH(autovacuum_enabledfalse);INSERTINTOorders(customer_id,status,amount,created_at)SELECTfloor(random()*1000000)::INT1,CASEWHENrandom()0.01THENcancelledELSEcompletedEND,round(random()*10000,2)::NUMERIC(12,2),NOW()-(random()*INTERVAL365 days)FROMgenerate_series(1,10000000);CREATEINDEXidx_orders_customer_idONorders(customer_id);-- ANALYZE 前EXPLAIN(ANALYZE,BUFFERS)SELECT*FROMordersWHEREcustomer_id500000;ANALYZEorders;-- ANALYZE 后EXPLAIN(ANALYZE,BUFFERS)SELECT*FROMordersWHEREcustomer_id500000;ALTERTABLEorders RESET(autovacuum_enabled);预期输出ANALYZE 前Estimated Rows ≈ 50000、扫描方式为Seq ScanANALYZE 后Estimated Rows ≈ 10、扫描方式为Index Scan。实际输出运行上述脚本第一条EXPLAIN返回Seq Scan on orders第二条返回Index Scan using idx_orders_customer_idpg_class.reltuples从-1变为约 10,000,000。共享读页数随硬件与填充因子变化本文未在写作环境中复测具体页数。常见失败ANALYZE 后计划仍为Seq Scan。原因通常是查询写成了WHERE customer_id 500000字符串字面量触发隐式转换列被表达式包裹而无法使用索引。修复方法改用整数字面量或让绑定参数类型与列一致同时用EXPLAIN确认过滤条件显示为customer_id $1而非customer_id::text $1。五、验证结果与边界指标ANALYZE 前ANALYZE 后reltuples-1~10,000,000n_distinctcustomer_id无记录~1,000,000Estimated Rows~50,000~10扫描方式Seq ScanIndex Scan失效边界同样需要记录假设 ETL 更新了 80 万行的customer_id低于 100 万行的自动阈值统计信息仍停留在旧分布。此时查WHERE customer_id 42优化器按旧选择率估算约 10 行而选择 Index Scan实际返回约 80 万行随机 I/O 被放大数万倍——统计过期时走索引反而比全表扫描更慢。运维上的判断信号是n_mod_since_analyze / reltuples接近 10% 即到达自动阈值边界可据此在变更量达到 7% 时提前告警在自动分析运行前介入。对customer_id这类高偏度列可用ALTER TABLE orders ALTER COLUMN customer_id SET STATISTICS 1000;提高采样精度代价是 ANALYZE 耗时与pg_statistic存储增加。思考把autovacuum_analyze_scale_factor降到 0.01 后大表上 auto-analyze 的 I/O 开销是否会成为新的抖动源ANALYZE修复后仍有查询走 Seq Scan如何区分正确代价判断与统计再次过期参考资料PostgreSQL 自动清理与 auto-analyze 配置文档12–16PostgreSQL ANALYZE 命令文档PostgreSQL 统计信息与监控视图文档

相关推荐

高精度计算入门与immense数据模型精讲(收官)
高精度计算入门与immense数据模型精讲(收官)

跳转链接 高精度计算入门与immense数据模型精讲(一)高精度计算入门与immense数据模型精讲(二)高精度计算入门与immense数据模型精讲(三)高精度计算入门与immense数据模型精讲(四)高… · 2026/11/5 7:56:53

程序卡住了?多进程多线程让你的 Python“多任务并行“ 跑起来
程序卡住了?多进程多线程让你的 Python“多任务并行“ 跑起来

程序运行慢,很多时候是它 "只肯一次干一件事"。这篇老哥带你搞懂 并发 / 并行、进程 / 线程,让程序真正 "多任务跑起来"。一、并发 vs 并行并发(Concurrency):单个 CPU 轮流处理多个任务&#xff… · 2026/11/5 7:55:52

跨平台移动自动化测试框架揭秘
跨平台移动自动化测试框架揭秘

拼课, 也就是大家所说的拼团课程, 其相关的数字标识为三五千七百七十九。:实现的移动端自动化测试框架到底是指什么样的含义?它是一个属于公开源代码的类型的, 能够同时在多个操作系统平台上进行移动应用自动化测试的工具和框架, 这个框架具备对iOS这一个平台, 以及… · 2026/11/5 7:55:52

10A太阳能充电控制器开源图纸:PWM直充与三段式充电调试
10A太阳能充电控制器开源图纸:PWM直充与三段式充电调试

简介:面向电子爱好者与嵌入式开发者的10A太阳能充电控制器开源方案,包含完整原理图与固件源码,适用于中小型光伏系统的学习、复刻与二次开发。压缩包共390个文件、约1.99MB,以c/h源码与工程配置为主,辅以sch原理图、pc… · 2026/11/5 8:21:05

自动驾驶NMPC与MPC路径规划控制技术解析
自动驾驶NMPC与MPC路径规划控制技术解析

1. 自动驾驶规划控制的核心挑战在自动驾驶系统开发中,规划控制模块直接决定了车辆行驶的平顺性和安全性。传统PID控制虽然简单易实现,但在复杂动态环境下往往表现不佳。这正是我们需要引入NMPC(非线性模型预测控制)和MPC&#xff… · 2026/11/5 8:21:05

Oracle到DuckDB日期函数迁移与连续区间计算实践
Oracle到DuckDB日期函数迁移与连续区间计算实践

1. 从Oracle到DuckDB:日期处理函数的迁移实践在数据库迁移或跨平台开发过程中,日期时间函数的兼容性处理是个高频痛点。最近我在处理一个VIP会员续费分析系统时,需要将原本运行在Oracle上的"连续会员区间计算"逻辑迁移到DuckDB。这… · 2026/11/5 8:21:05

C#解析.rofl回放元数据:LeagueReplayParser批量落库
C#解析.rofl回放元数据:LeagueReplayParser批量落库

简介:LeagueReplayParser 是面向《英雄联盟》重放解析的 C# 类库,可读取文档目录下扩展名为 .rofl 的重放文件,并围绕客户端版本与重放启动提供基础能力,适合研究比赛统计、回放分析或客户端重放流程的开发者参考;解析… · 2026/11/5 8:21:05

拆解安全厂商17K*15:公积金基数、年终奖与谈薪避坑
拆解安全厂商17K*15:公积金基数、年终奖与谈薪避坑

拿到 Offer 了!某安全厂商 17K15——这个数字组合在安全圈的校招和社招群里一出现,讨论热度基本能顶到当天最高。17K 的月薪基数、15 个月的年薪包,听起来是应届生里相当能打的一档,不少人第一反应就是"年包 25.5 万&#xf… · 2026/11/5 8:21:05

四轴机械臂反解实战:牛顿迭代法的MATLAB实现与嵌入式移植
四轴机械臂反解实战:牛顿迭代法的MATLAB实现与嵌入式移植

简介:本资源面向机器人学初学者与MATLAB实践者,聚焦四轴机械臂逆运动学核心问题——关节反解,系统讲解雅可比矩阵原理、牛顿迭代法数值求解流程及其实现要点。资源包含1个MATLAB仿真脚本(.m)与1份英文权威参考资料PDF&… · 2026/11/5 8:20:04

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38

多智能体协作系统架构设计与实践优化
多智能体协作系统架构设计与实践优化

1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中… · 2026/11/5 0:00:39

大模型系统性入门实战指南:从原理到部署微调的全链路路线
大模型系统性入门实战指南:从原理到部署微调的全链路路线

提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#… · 2026/11/5 0:00:39

鸿蒙文件系统沙箱机制解析与开发实践
鸿蒙文件系统沙箱机制解析与开发实践

1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的… · 2026/11/5 0:00:39

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码