二、开启 ACS 可能带来的隐患
ACS 不是独立租户开关,而是参数化 + Plan Cache 上的自适应能力,风险主要来自多计划共存与反馈学习。
1. 计划缓存与内存压力
- 同一参数化 SQL 可能缓存多个计划 +
ACS_SEL_INFO 元数据。
- Plan Cache 体积上升,淘汰更频繁,可能连带影响其他 SQL 命中率。
2. 性能抖动与“学习期”劣化
- 首次或进入新选择率区间时可能硬解析生成新计划。
- 反馈未稳定前,可能出现“这次快、下次慢”的抖动。
-
ADAPTIVE_FEEDBACK_TIMES 反映连续正/负反馈,说明存在演进过程。
3. 无法根治“大小账号”
官方 FAQ 对“大小账号”的描述与 ACS 场景高度重合:
200.sql-related-problems-faq.mdLines 371-377
“大小账号”问题是指在SQL语句解析和执行过程中,由于相同的SQL ID对应不同的执行计划,导致不同查询条件下的性能表现差异显著。例如,当某条SQL语句被参数化后,计划缓存中可能存在一个不适合所有情况的执行计划。这会造成以下影响:
当系统首次执行某条SQL(如select * from items where store = 'taobao';)时,执行计划(p1)可能是全表扫描,适用于选择率极低情况。
如果之后执行另一条参数化SQL(如select * from items where store = 'xiaomaibu';),且选择率很高,原本的计划p1可能就不再是最优方案,导致性能下降。
ACS 在“存在可接受的单一计划”时可能缓解;若小/大账号需要完全不同的路径(索引 vs 全表、不同 join 顺序),ACS 仍可能:
- 学习慢、选错区间;
- 区间内计划仍非最优;
- 与 SPM 演进、计划淘汰策略交织,排查更难。
4. 统计信息与反馈失真
- 估行/选择率偏差会导致 ACS 划错区间或选错计划。
- 数据分布突变后,旧区间计划可能长期“半生效”。
5. 运维与诊断复杂度
- 同一
SQL_ID 对应多个 PLAN_HASH,需结合 ACS_SEL_INFO、QUERY_SQL、PARAM_INFOS 分析。
- Outline/SPM 固定计划时,可能与 ACS 多计划逻辑产生预期外交互(代码中曾有
is_spm_acs_closed_ 相关分支)。
6. 与 SPM 的叠加效应
V4.2.1+ 建议用 SPM 缓解大小账号;SPM 在线演进 + ACS 多计划,会同时改变“用哪个计划”,问题定位需同时看 baseline 与 Plan Cache。
三、哪些业务场景应“关闭 ACS 效果”
OceanBase 没有单独的 enable_acs 租户参数;实践中通过关闭参数化或禁止计划复用来规避 ACS:
| 手段 |
作用 |
SET cursor_sharing = 'EXACT'(全局/会话) |
文本 SQL 不做 Fast Parser 参数化 |
/*+ CURSOR_SHARING_EXACT */ |
单条 SQL 禁止参数化 |
/*+ USE_PLAN_CACHE(NONE) */ / ob_enable_plan_cache=0
|
不缓存计划,ACS 无从谈起 |
| Outline 绑定固定计划 |
稳定执行路径,避免自适应切换 |
建议优先考虑关闭参数化/ACS 的场景:
- 典型大小账号:同一模板下过滤性差异极大(租户 ID、店铺、日期范围等),且无法用单一计划兼顾。
- 已做执行计划绑定:Outline、SPM
FIXED baseline、SQL Plan Baseline 要求计划稳定可复现。
- 压测/对账/回放:需要相同 SQL 文本 → 相同
PLAN_HASH,排除自适应干扰。
- 计划抖动已观测:
GV$OB_PLAN_CACHE_PLAN_STAT 中 IS_BIND_AWARE=1 且 PLAN_HASH 频繁变化、RT 波动大。
- 统计信息不可靠:新表、直方图缺失、大批量加载后尚未收集统计信息。
- Plan Cache 紧张:内存告警、频繁淘汰,多计划加剧问题。
对大账号侧,官方更推荐 CURSOR_SHARING_EXACT Hint 单独保留计划,而不是全局关 ACS:
200.sql-related-problems-faq.mdLines 428-429
四、cursor_sharing 与 enable_ps_parameterize 的关系
二者不是同一开关,作用路径不同:
| 维度 |
cursor_sharing |
enable_ps_parameterize |
| 类型 |
系统变量(Global/Session) |
租户配置项(V4.3.5 BP4+) |
| 默认值 |
FORCE |
True |
| 作用对象 |
文本 SQL(Fast Parser / 普通查询) |
PS(Prepared Statement) 解析阶段 |
| 关闭时行为 |
EXACT → get_enable_exact_mode() 为真,跳过 Fast Parser 参数化 |
false → PS 不走参数化进 PS Cache |
ob_basic_session_info.hLines 1459-1461
bool get_enable_exact_mode() const
{
return sys_vars_cache_.get_cursor_sharing_mode() == ObCursorSharingMode::EXACT_MODE;
ob_sql.cppLines 1414-1422
} else if (session.is_enable_ps_parameterize() && pc_ctx.ps_need_parameterized_) {
info_ctx.ps_need_parameterization_ = true;
…
} else {
info_ctx.ps_need_parameterization_ = false;
ob_plan_cache.cppLines 722-731
bool enable_exact_mode = pc_ctx.sql_ctx_.session_info_->get_enable_exact_mode();
…
} else if (enable_exact_mode) {
(void)fp_result.pc_key_.name_.assign_ptr(raw_sql.ptr(), raw_sql.length());
结论:二者不必强行保持一致,因为文本链路与 PS 链路本来就分开设计。
五、是否“配置一致”为最优?
不一定。 更合理的做法是按接入方式分别配置。
常见推荐组合
| 场景 |
cursor_sharing |
enable_ps_parameterize |
说明 |
| 通用 OLTP(文本 + PS 混用) |
FORCE |
True |
最大 Plan Cache 复用;ACS 对两类入口均可能生效 |
| 以 PS 为主、需与客户端文本严格一致 |
FORCE |
False |
文本仍参数化;PS 保持 no_param_sql 与 checksum 一致(见 ob_sql.cpp 注释) |
| 大小账号严重、以文本 SQL 为主 |
EXACT 或 Hint |
视 PS 是否也有大小账号 |
文本侧独立计划;PS 可单独 True 若仅 PS 有该问题 |
| 新建租户(当前代码默认) |
通常仍为 FORCE
|
False(创建租户时写入) |
说明 PS 参数化在部分场景下更易出问题 |
ob_tenant_ddl_service.cppLines 2282-2315
ObString config_name_enable_ps_parameterize(“enable_ps_parameterize”);
ObString config_value_enable_ps_parameterize(“false”);
…
} else if (OB_FAIL(tenant_init_config.add_config(config_name_enable_ps_parameterize, config_value_enable_ps_parameterize))) {
需要刻意不一致的特殊场景
- 文本 SQL 要共享计划,PS 要保持字面量 SQL
-
cursor_sharing=FORCE + enable_ps_parameterize=false
- 典型:JDBC/OCI Prepare 后 Execute 需与客户端原始 SQL 一致。
- 仅文本 SQL 存在大小账号,PS 绑定值分布稳定
- 大账号文本 SQL 用
CURSOR_SHARING_EXACT;PS 保持 enable_ps_parameterize=true 以复用计划。
- 仅 PS 存在参数化问题(如部分函数/JSON 路径不能参数化)
-
enable_ps_parameterize=false,cursor_sharing 仍可 FORCE。
- 部分语句会因
ps_need_parameterized_=false 自动跳过(如 concat、json_* 等)。
- 压测基线 + 生产 PS
- 压测会话
cursor_sharing=EXACT;生产租户 PS 开启参数化。