granularity => 'partition' 更慢的主因是默认串行执行、分区级字典锁争用及元数据开销;必须配 degree 才能并行,且增量统计需 granularity => 'auto' 或 'all' 才生效。

为什么 GRANULARITY => 'PARTITION' 反而更慢
默认串行执行是主因。即使你只指定一个分区,DBMS_STATS.GATHER_TABLE_STATS 在 GRANULARITY => 'PARTITION' 模式下仍会逐个分区顺序处理,除非你显式加 DEGREE 参数。更隐蔽的问题是:每个分区收集过程都要争抢同一把统计字典锁(sys.col$ 相关),高并发时锁等待堆积,实际耗时可能比单线程还长。
常见错误是以为“只扫一个分区=快”,却忽略了元数据扫描、锁争用和上下文切换开销。实测中,100+ 分区的表,GRANULARITY => 'GLOBAL' + DEGREE => 8 往往比 'PARTITION' 快得多——因为避免了重复解析分区定义和反复加锁。
- 必须配
DEGREE => DBMS_STATS.AUTO_DEGREE或具体数值(如4)才能真正并行 - 分区数远超 CPU 核数时,并行反而拖慢;建议分区数 ≤
DEGREE × 2 -
GRANULARITY => 'PARTITION'下即使cascade => TRUE,也会为每个分区单独收集索引统计,放大开销
INCREMENTAL = TRUE 却没提速,甚至更慢
增量统计不是开个开关就生效的魔法。它依赖 GRANULARITY => 'AUTO' 或 'ALL' 才能触发 synopsis 复用逻辑;设成 'GLOBAL' 会直接跳过分区扫描,导致后续所有增量都失效——表面看 LAST_ANALYZED 更新了,但 NUM_ROWS 等全局值仍是旧的。
哈希分区尤其危险:INCREMENTAL 在哈希表上几乎无收益。新数据天然打散到所有分区,每次都要读取全部 synopsis 并合并,额外开销常超过全量采样。范围/列表分区才适合:每天只新增或更新一两个分区,其余分区统计长期有效。
- 第一次启用前必须先跑一次
granularity => 'ALL',否则没有初始 synopsis 链 - 验证变更分区数:
SELECT PARTITION_NAME, LAST_ANALYZED FROM DBA_TAB_PARTITIONS WHERE TABLE_NAME = 'T',若多个分区时间接近,说明增量意义不大 - 哈希分区建议保持
INCREMENTAL => 'FALSE',改用ESTIMATE_PERCENT => 10加速采样
全局统计被标记为 STALE_STATS = 'YES' 后持续拖慢
只收集部分分区(比如用 partname => 'P202608')后,DBA_TABLES 中该表的全局行会立刻变成 STALE_STATS = 'YES',哪怕 LAST_ANALYZED 时间更新了。Oracle 认为全局统计必须基于全部分区重算才可信,局部更新直接降级其有效性。
这个标记不会因 LOCK_TABLE_STATS 或 NO_INVALIDATE => FALSE 改变。后果是:优化器在跨分区查询时可能退化为保守估算,执行计划波动大,且后续自动任务会反复尝试“修复”这个 stale 状态,形成恶性循环。
- 修复只有两种方式:
GRANULARITY => 'ALL'全量收集,或手动用DBMS_STATS.SET_TABLE_STATS强制写入已知准确的全局值 -
GRANULARITY => 'PARTITION'不等于“安全局部更新”——它是主动让全局统计失准的设计 - 业务高峰期看到大量 SQL 执行计划突变,第一反应应查
DBA_TABLES.STALE_STATS
自动收集任务在业务高峰触发锁表阻塞
Oracle 自动统计信息收集(auto optimizer stats collection)默认在维护窗口运行,但若窗口设置不当或表过大,可能延续到业务时段。此时 DBMS_STATS 会持有表级锁和字典锁,导致 DML 阻塞、会话排队,典型现象是 AWR 报告中 enq: TX - row lock contention 或 library cache lock 等待陡增。
曾有案例:某金融系统在 18:30–19:00 出现整体响应变慢,定位发现正是对最大分区(MAXVALUE)做统计收集,大量查询被迫等待锁释放。问题不在收集本身,而在时机与对象选择失控。
- 禁用自动收集后手工调度,比依赖默认窗口更可控
- 对超大分区表,用
partname显式指定新分区,避开历史大分区 - 收集前查
DBA_SEGMENTS确认段大小,99G 表用ESTIMATE_PERCENT => 10而非100,节省 70%+ 时间
关键点其实就两个:粒度参数不是描述“收集范围”,而是控制“统计有效性传播路径”;时间消耗大户往往不在数据扫描,而在字典锁争用和元数据一致性校验。别信“只扫一个分区就快”的直觉。











