隐含参数 _b_tree_bitmap_plans 导致 SQL 执行计划劣化

问题现象:同一关键 SQL,一厂平均执行 12ms,三厂平均执行 700ms(三厂数据量更小)

根因:三厂数据库设置了隐含参数 _b_tree_bitmap_plans=FALSE,禁用了 BITMAP CONVERSION TO ROWIDS 访问路径,优化器退化为全表扫描

解决方案:通过 SQL Profile 为三厂绑定含 BITMAP CONVERSION 的较优执行计划,执行时间降至 1ms 以内

1. 问题现象

业务反馈某个关键 SQL 在一厂和三厂的执行时间差距较大。三厂数据量更小,理论上应该更快,但实际表现相反。

1.1 执行时间对比

工厂

平均执行时间

执行计划

一厂

~12ms

BITMAP CONVERSION TO ROWIDS(索引访问)

三厂

~700ms

FULL TABLE SCAN(全表扫描)

1.2 执行计划差异

一厂执行计划

三厂执行计划

关键差异访问路径就是一厂走的BITMAP 三厂走的全表

2. 根因分析

2.1 关键参数

三厂为新建工厂,数据库实施参数标准中配置了隐含参数_b_tree_bitmap_plans = FALSE。该参数在 OLTP 最佳实践中建议设为 FALSE,但在本案例中恰好阻止了优化器选择最优执行计划。

参数说明:_b_tree_bitmap_plans 控制优化器是否考虑 BITMAP CONVERSION TO ROWIDS / FROM ROWIDS 以及 BITMAP AND/OR/MINUS 等执行计划。

默认为TRUE(允许),设为FALSE后所有 B-tree 索引转 Bitmap 的访问路径均被禁用。

2.2 影响链路

一厂执行计划访问路径

h := SYS.SQLPROF_ATTR( q'[BEGIN_OUTLINE_DATA]', q'[IGNORE_OPTIM_EMBEDDED_HINTS]', q'[OPTIMIZER_FEATURES_ENABLE('19.1.0')]', q'[DB_VERSION('19.1.0')]', q'[OPT_PARAM('_optimizer_extended_cursor_sharing' 'none')]', q'[OPT_PARAM('_optimizer_extended_cursor_sharing_rel' 'none')]', q'[OPT_PARAM('_optimizer_adaptive_cursor_sharing' 'false')]', q'[OPT_PARAM('_optimizer_use_feedback' 'false')]', q'[OPT_PARAM('_optimizer_gather_feedback' 'false')]', q'[ALL_ROWS]', q'[OUTLINE_LEAF(@"SEL$1")]', q'[OUTLINE_LEAF(@"SEL$2")]', q'[NO_ACCESS(@"SEL$2" "from$_subquery$_002"@"SEL$2")]', q'[BITMAP_TREE(@"SEL$1" "LX"@"SEL$1" OR(1 1 ("TEST"."SN") 2 ("TEST"."SUBSN") 3 ("TEST"."XPSN")))]', q'[BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$1" "LX"@"SEL$1")]', q'[END_OUTLINE_DATA]'); :signature := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt); :signaturef := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt, TRUE);

三厂执行计划访问路径

h := SYS.SQLPROF_ATTR( q'[BEGIN_OUTLINE_DATA]', q'[IGNORE_OPTIM_EMBEDDED_HINTS]', q'[OPTIMIZER_FEATURES_ENABLE('19.1.0')]', q'[DB_VERSION('19.1.0')]',q'[OPT_PARAM('_b_tree_bitmap_plans' 'false')]', --该隐含参数阻止了优化器选择BITMAPq'[OPT_PARAM('_optim_peek_user_binds' 'false')]', q'[OPT_PARAM('_bloom_filter_enabled' 'false')]', q'[OPT_PARAM('_optimizer_extended_cursor_sharing' 'none')]', q'[OPT_PARAM('_optimizer_outer_to_anti_enabled' 'false')]', q'[OPT_PARAM('_bloom_pruning_enabled' 'false')]', q'[OPT_PARAM('_optimizer_extended_cursor_sharing_rel' 'none')]', q'[OPT_PARAM('_optimizer_adaptive_cursor_sharing' 'false')]', q'[OPT_PARAM('_and_pruning_enabled' 'false')]', q'[OPT_PARAM('_optimizer_use_feedback' 'false')]', q'[OPT_PARAM('_px_adaptive_dist_method' 'off')]', q'[OPT_PARAM('_optimizer_strans_adaptive_pruning' 'false')]', q'[OPT_PARAM('_optimizer_null_accepting_semijoin' 'false')]', q'[OPT_PARAM('_optimizer_gather_feedback' 'false')]', q'[OPT_PARAM('_optimizer_reduce_groupby_key' 'false')]', q'[OPT_PARAM('_optimizer_nlj_hj_adaptive_join' 'false')]', q'[ALL_ROWS]', q'[OUTLINE_LEAF(@"SEL$1")]', q'[OUTLINE_LEAF(@"SEL$2")]', q'[NO_ACCESS(@"SEL$2" "from$_subquery$_002"@"SEL$2")]', q'[FULL(@"SEL$1" "LX"@"SEL$1")]', q'[END_OUTLINE_DATA]'); :signature := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt); :signaturef := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt, TRUE);

2.3 为何 OLTP 建议设为 FALSE

该参数设为 FALSE 的初衷是避免 OLTP 场景下产生不合适的 Bitmap 转换计划。当 SQL 包含多个 B-tree 索引条件(尤其是星型转换、多索引 AND/OR,本案例sql为多个or查询)时,优化器可能生成次优的 BITMAP CONVERSION 计划。此外,19c 中存在已知 Bug:

  • Bug 30102774— ORA-7445 [kkosbn] Error With SQL With Bitmap Plans

设为 FALSE 可作为 workaround 规避该类 Bug。但对于需要使用 BITMAP CONVERSION 的特定 SQL,该设置会产生负面影响。

3. 解决方案

3.1 方案选择

最简单且影响最小的方式是使用SQL Profile为该 SQL 绑定含 BITMAP CONVERSION 的较优执行计划,无需修改全局参数,不影响其他 SQL 的执行计划。

3.2 一厂 SQL Profile Outline(较优计划)

从一厂获取该 SQL 的较优执行计划 Outline,通过 SQL Profile 绑定到三厂。关键 Hint 如下:

  • BITMAP_TREE(@"SEL$1" "LX"@"SEL$1" OR(1 1 ("TEST"."SN") 2 ("TEST"."SUBSN") 3 ("TEST"."XPSN")))

  • BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$1" "LX"@"SEL$1")

一厂 Outline 中包含的优化器参数绑定:

  • OPT_PARAM('_optimizer_extended_cursor_sharing' 'none')

  • OPT_PARAM('_optimizer_extended_cursor_sharing_rel' 'none')

  • OPT_PARAM('_optimizer_adaptive_cursor_sharing' 'false')

  • OPT_PARAM('_optimizer_use_feedback' 'false')

  • OPT_PARAM('_optimizer_gather_feedback' 'false')

3.3 三厂当前 SQL Profile Outline(较差计划)

三厂执行计划 Outline 中包含的关键差异:

  • OPT_PARAM('_b_tree_bitmap_plans' 'false')— 直接导致无法使用 BITMAP CONVERSION

  • FULL(@"SEL$1" "LX"@"SEL$1")— 全表扫描(替换了 BITMAP_TREE)

此外还包含以下参数绑定:

  • OPT_PARAM('_optim_peek_user_binds' 'false')

  • OPT_PARAM('_bloom_filter_enabled' 'false')

  • OPT_PARAM('_bloom_pruning_enabled' 'false')

  • OPT_PARAM('_and_pruning_enabled' 'false')

  • OPT_PARAM('_optimizer_outer_to_anti_enabled' 'false')

  • OPT_PARAM('_optimizer_null_accepting_semijoin' 'false')

  • OPT_PARAM('_optimizer_reduce_groupby_key' 'false')

  • OPT_PARAM('_optimizer_nlj_hj_adaptive_join' 'false')

  • OPT_PARAM('_px_adaptive_dist_method' 'off')

  • OPT_PARAM('_optimizer_strans_adaptive_pruning' 'false')

3.4 效果验证

阶段

执行计划

平均执行时间

优化前(三厂原始)

FULL TABLE SCAN

~700ms

一厂参考值

BITMAP CONVERSION TO ROWIDS

~12ms

优化后(绑定 SQL Profile)

BITMAP CONVERSION TO ROWIDS

小于 1ms

绑定 SQL Profile 后,三厂该 SQL 的执行时间从 700ms 降至 1ms 以内,性能提升约700 倍

4. _b_tree_bitmap_plans 参数详解

4.1 控制范围

该隐藏参数控制优化器是否考虑以下执行计划:

  • BITMAP CONVERSION TO ROWIDS

  • BITMAP CONVERSION FROM ROWIDS

  • BITMAP AND / OR / MINUS

这类 B-tree 索引转 Bitmap 再运算的执行计划。

4.2 参数值说明

参数值

行为

TRUE(默认)

允许优化器使用 BITMAP CONVERSION 相关计划

FALSE

禁止所有 BITMAP CONVERSION 计划,不再出现 BITMAP CONVERSION TO ROWIDS 等路径

4.3 典型执行计划场景

当 SQL 包含多个 B-tree 索引条件(尤其是星型转换、多索引 AND/OR)时,优化器可能生成如下计划:

  • BITMAP CONVERSION TO ROWIDS

  • BITMAP AND

  • BITMAP CONVERSION FROM ROWIDS - INDEX RANGE SCAN

  • BITMAP CONVERSION FROM ROWIDS - INDEX RANGE SCAN

将 _b_tree_bitmap_plans 设为 FALSE 后,上述计划全部被禁用。

4.4 查看与修改

  • 查看当前值:

  • select x.ksppinm name, y.ksppstvl value, y.ksppstdf isdefault, decode(bitand(y.ksppstvf, 7), 1, 'MODIFIED', 4, 'SYSTEM_MOD', 'FALSE') ismod, decode(bitand(y.ksppstvf, 2), 2, 'TRUE', 'FALSE') isadj from sys.x$ksppi x, sys.x$ksppcv y where x.inst_id = userenv('Instance') and y.inst_id = userenv('Instance') and x.indx = y.indx and x.ksppinm like '%b_tree_bitmap%' order by translate(x.ksppinm, ' _', ' ');
  • 会话级测试:ALTER SESSION SET "_b_tree_bitmap_plans" = FALSE;

  • Hint方式禁用/启用:

    SELECT /*+ OPT_PARAM('_b_tree_bitmap_plans', 'TRUE') */ SELECT /*+ OPT_PARAM('_b_tree_bitmap_plans', 'FALSE') */
  • 实例级修改(需重启):ALTER SYSTEM SET "_b_tree_bitmap_plans" = FALSE SCOPE=SPFILE;

5. 经验总结

1. 参数标准不能一刀切

OLTP 最佳实践中建议禁用 _b_tree_bitmap_plans 以规避已知 Bug 和次优计划,但需评估业务 SQL 是否依赖 BITMAP CONVERSION 路径。新建工厂实施参数标准时,建议先用一厂的执行计划基线做回归测试。

2. SQL Profile 是精准调优利器

当全局参数调整会影响其他 SQL 时,SQL Profile 可以针对单条 SQL 绑定最优执行计划,影响范围最小。适合「大部分 SQL 正常,个别 SQL 受影响」的场景。

3. 隐含参数变更需评估影响面

修改隐含参数前,建议在测试环境对关键 SQL 做执行计划对比(explain plan / SQL Tuning Advisor),确认不会产生回归。