ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

TaoToken 视角:adaptive cursor sharing 与 SQL Plan Management 如何协同?

TaoToken 视角:adaptive cursor sharing 与 SQL Plan Management 如何协同? 1. 当 ACS 遇上 SPM一个 DBA 绕不开的游标共享难题adaptive cursor sharingACS自适应游标共享和 SQL Plan ManagementSPMSQL 计划管理是 Oracle 里两个经常被同时提起、却又容易让人混淆的特性。简单说ACS 管的是「这次执行要不要复用已有的子游标」SPM 管的是「优化器允许从哪些计划里挑」。一个负责游标共享的粒度一个负责计划集合的边界。很多 DBA 在调优时发现明明开了 ACS绑定变量窥探也正常可执行计划还是飘或者明明 SPM 里锁定了 baseline某个绑定值下性能还是突然塌方。这类问题的根子往往就出在两者交互的细节上。这篇文章面向正在做 SQL 调优的 DBA 和运维同学聚焦 Oracle 中 adaptive cursor sharing 与 SQL Plan Management 的交互机制。我会给出可复制的初始化参数配置、SQL 执行计划捕获与验证脚本并演示如何通过 TaoToken 统一 Key/API 通道接入诊断流程验证两者在游标共享与计划稳定性上的协同效果。如果你手头正好有一套测试库跟着做一遍基本能把这两个特性的边界摸清楚。先说结论性的认知ACS 决定「是否共享子游标」SPM 决定「优化器能选哪些计划」。当一次执行因为绑定值不匹配而触发硬解析时优化器被叫来重新优化此时 SPM 会约束它的选择范围而不管这次优化是不是 ACS 触发的。理解这句话后面所有的现象都能解释通。2. TaoToken 前置准备统一 Key 与 API 通道接入诊断流程在开始动手之前先把诊断流程的「通道」搭好。我习惯把 SQL 调优过程中产生的诊断脚本、执行计划文本、AWR 片段统一走一个 API 通道做归档和二次分析这样跨库对比时不用来回切客户端。TaoToken 在这里扮演的角色是统一 Key/API 通道你申请一个 Key就能通过同一套接口调用模型对话、coding-plan 等能力把诊断脚本的生成、执行计划的解读、报错信息的排查串起来。第一步打开官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册并登录。登录后进入控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。在控制台里找到 API Keys 页面路径是 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 点「创建 Key」复制出来保存好。这个 Key 就是你后续所有请求的凭证。第二步确认 API 入口。TaoToken 的 API 基址是 https://taotoken.net/api 注意这个地址不带 UTM 参数直接用于程序里的 Base URL 配置。如果你用的是兼容 OpenAI 协议的客户端Base URL 填 https://taotoken.net/api 即可Key 填刚才复制的那串。第三步选模型。做 SQL 调优辅助时我一般用模型对话能力来解读执行计划文本入口在 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 。如果你要长期跑编码类或 Agent 类任务比如自动生成诊断脚本、批量解析 trace 文件可以看 coding-planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 遇到接口细节先翻这里。这里要提醒一句TaoToken 是统一 Key/API 通道不是数据库客户端也不替代 SQL*Plus 或 SQL Developer。它的价值在于把诊断过程中零散的文本分析、脚本生成、报错排查收敛到一个入口减少你在多个工具之间复制粘贴的成本。下面进入正题先配参数。3. 可复制配置初始化参数与 SPM baseline 捕获脚本这一节给出可以直接粘贴执行的配置。先看初始化参数。ACS 和 SPM 各自有开关建议在测试库上先全开观察行为后再按需调整。-- 查看当前 ACS 与 SPM 相关参数 SHOW PARAMETER optimizer_adaptive_features; SHOW PARAMETER optimizer_adaptive_reporting_only; SHOW PARAMETER optimizer_capture_sql_plan_baselines; SHOW PARAMETER optimizer_use_sql_plan_baselines; -- 测试环境建议配置生产请评估后再改 ALTER SYSTEM SET optimizer_adaptive_features TRUE SCOPE BOTH; ALTER SYSTEM SET optimizer_adaptive_reporting_only FALSE SCOPE BOTH; ALTER SYSTEM SET optimizer_capture_sql_plan_baselines TRUE SCOPE BOTH; ALTER SYSTEM SET optimizer_use_sql_plan_baselines TRUE SCOPE BOTH;optimizer_adaptive_features打开后ACS 才会在运行时根据绑定值决定是否复用子游标。optimizer_capture_sql_plan_baselines设为 TRUE 时优化器会自动把重复执行的 SQL 计划捕获进 baseline这是 SPM 自动加载计划的入口。optimizer_use_sql_plan_baselines控制优化器是否使用已存在的 baseline。接下来构造一张有数据倾斜的表模拟真实场景。这里用 job 列做倾斜让不同绑定值的选择性差异足够大ACS 才会明显介入。-- 建表并制造倾斜数据 CREATE TABLE employees_acs AS SELECT employee_id, first_name, last_name, email, job_id, department_id, salary FROM employees; -- 放大数据量并制造 job_id 倾斜 INSERT INTO employees_acs SELECT employee_id 1000, first_name, last_name, email, job_id, department_id, salary FROM employees_acs; -- 重复执行几次 INSERT ... SELECT 自身把数据量滚到几十万行 COMMIT; -- 制造极端倾斜AD_PRES 只有 1 行SA_REP 占绝大多数 UPDATE employees_acs SET job_id AD_PRES WHERE employee_id 100; UPDATE employees_acs SET job_id SA_REP WHERE job_id NOT IN (AD_PRES, AD_VP); COMMIT; -- 建索引 CREATE INDEX idx_emp_acs_job ON employees_acs(job_id); -- 收集统计信息 EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, EMPLOYEES_ACS, CASCADE TRUE);数据准备好后用带绑定变量的查询跑三次不同绑定值观察 ACS 是否生成多个子游标。这里加BIND_AWARE提示是为了加速 bind-aware 游标进入游标缓存的过程。-- 带绑定变量的查询加 BIND_AWARE 提示 SELECT /* BIND_AWARE */ d.department_name, COUNT(*), SUM(e.salary) FROM employees_acs e, departments d WHERE e.department_id d.department_id AND e.job_id :job GROUP BY d.department_name; -- 依次用三个绑定值执行 VAR job VARCHAR2(20); EXEC :job : AD_PRES; SELECT /* BIND_AWARE */ d.department_name, COUNT(*), SUM(e.salary) FROM employees_acs e, departments d WHERE e.department_id d.department_id AND e.job_id :job GROUP BY d.department_name; EXEC :job : AD_VP; -- 重复上面的 SELECT EXEC :job : SA_REP; -- 重复上面的 SELECT执行完后查游标缓存看子游标数量和是否 bind-awareSELECT child_number, executions, is_bind_sensitive, is_bind_aware, plan_hash_value FROM v$sql WHERE sql_text LIKE %BIND_AWARE%employees_acs% ORDER BY child_number;正常情况下三个绑定值会对应不同的 plan_hash_valueis_bind_aware为 Y。这就是 ACS 在起作用它认为不同绑定值需要不同计划于是允许生成多个子游标。现在把其中两个计划加载进 SPM baseline。从游标缓存手动加载是最直观的方式-- 从游标缓存加载指定 plan_hash_value 的计划到 baseline DECLARE l_plans PLS_INTEGER; BEGIN l_plans : DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE( sql_id 你的sql_id, plan_hash_value 你的plan_hash_value_1, enabled YES ); DBMS_OUTPUT.PUT_LINE(Loaded plans: || l_plans); END; / -- 再加载第二个计划 DECLARE l_plans PLS_INTEGER; BEGIN l_plans : DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE( sql_id 你的sql_id, plan_hash_value 你的plan_hash_value_2, enabled YES ); DBMS_OUTPUT.PUT_LINE(Loaded plans: || l_plans); END; /加载后查 baselineSELECT sql_handle, plan_name, enabled, accepted, fixed, reproduced FROM dba_sql_plan_baselines WHERE sql_text LIKE %BIND_AWARE%employees_acs%;到这里配置和捕获就完成了。关键点在于baseline 里只放了两个计划第三个绑定值对应的计划不在其中。接下来验证交互效果。4. 验证请求与成功结果ACS 与 SPM 协同下的计划选择把 baseline 建好之后重新按 AD_PRES、AD_VP、SA_REP 的顺序执行查询观察每次实际选中的计划。这一步是理解交互的核心。先清一下共享池里的相关游标避免旧游标干扰测试库操作生产慎用-- 仅测试环境使用 ALTER SYSTEM FLUSH SHARED_POOL;然后依次执行三个绑定值每次执行后查实际计划EXEC :job : AD_PRES; SELECT /* BIND_AWARE */ d.department_name, COUNT(*), SUM(e.salary) FROM employees_acs e, departments d WHERE e.department_id d.department_id AND e.job_id :job GROUP BY d.department_name; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(FORMAT TYPICAL PEEKED_BINDS));AD_PRES 这次会命中 baseline 里已接受的计划因为该计划本来就在 baseline 中优化器被允许选择它。结果和没有 SPM 时一致。接着执行 AD_VPEXEC :job : AD_VP; SELECT /* BIND_AWARE */ d.department_name, COUNT(*), SUM(e.salary) FROM employees_acs e, departments d WHERE e.department_id d.department_id AND e.job_id :job GROUP BY d.department_name; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(FORMAT TYPICAL PEEKED_BINDS));这里就是交互的关键点。优化器针对 AD_VP 算出了一个不在 baseline 里的计划但 SPM 不允许直接使用它于是退而选择 baseline 中代价最优的已接受计划通常是 hash join 那个。同时优化器算出的这个新计划会被自动加入 baseline但状态是 unaccepted要等 evolve 之后才会被考虑。最后执行 SA_REPEXEC :job : SA_REP; SELECT /* BIND_AWARE */ d.department_name, COUNT(*), SUM(e.salary) FROM employees_acs e, departments d WHERE e.department_id d.department_id AND e.job_id :job GROUP BY d.department_name; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(FORMAT TYPICAL PEEKED_BINDS));SA_REP 会命中 baseline 里为它加载的那个计划和没有 SPM 时一致。现在查游标缓存看子游标数量的变化SELECT child_number, executions, is_bind_sensitive, is_bind_aware, plan_hash_value FROM v$sql WHERE sql_text LIKE %BIND_AWARE%employees_acs% ORDER BY child_number;你会发现一个有意思的现象AD_VP 和 SA_REP 最终选了同一个计划同一个 plan_hash_value因此它们共享同一个子游标。也就是说baseline 的存在减少了子游标数量和硬解析次数。原本在没有 SPM 时AD_VP 可能生成独立子游标现在被收敛到了已有计划上。这就是协同效果ACS 负责判断「这个绑定值能不能复用现有子游标」SPM 负责在必须硬解析时「把优化器的选择限制在 baseline 内」。两者叠加游标共享更稳定计划漂移被压制。如果你想通过 TaoToken 的模型对话能力来解读上面这些执行计划文本可以把DBMS_XPLAN的输出贴到 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 对应的对话里让它帮你标注每一步的成本变化和连接方式。实测下来对于识别 hash join 与 nested loop 切换的临界点挺有帮助。5. 本篇常见错排查401、local proxy failed 与游标找不到调优过程中踩的坑往往不在 SQL 本身而在环境和工具链。这一节把几个高频报错列出来对照排查。第一个是 401。如果你在用脚本调 TaoToken 的 API 做诊断文本分析返回 401 基本是 Key 的问题。检查三点Key 是否复制完整有没有漏字符、请求头里Authorization: Bearer 你的Key格式对不对、Base URL 是不是写成了 https://taotoken.net/api 。注意 Base URL 不要带 UTM 参数带参数的地址是给浏览器访问的程序里用纯净的 API 地址。Key 在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 重新生成一个再试。第二个是 local proxy failed。这个报错通常出现在客户端配置了本地代理但代理没起来或者代理端口写错。排查顺序先确认本地代理进程是否在跑再确认客户端里的代理地址和端口是否和实际一致。如果你根本没打算走代理就把客户端的代理配置清空直连 https://taotoken.net/api 。很多时候是之前配过代理忘了删导致请求发不出去。第三个是 reading choices 相关报错。这类错误一般出现在解析模型返回结构时返回体里没有预期的choices字段。原因可能是请求体格式不对比如model字段填了不存在的模型 ID或者messages结构写错。对照文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 检查请求 JSON。如果你用的是 Claude Code 或 Anthropic 兼容接口注意入口是 https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude-code-anthropicutm_campaignrewrite 协议细节和 OpenAI 那套不一样。第四个是 OAuth 相关报错。如果你在配置 Codex 的 auth.json或者用 CC Switch、Cline MCP 这类工具出现 OAuth 报错通常是凭证文件里的字段缺失或过期。这时候要把三件套写全Base URL 填 https://taotoken.net/api Key 填你的 API KeyModel ID 填你在模型列表里选定的那个。三者缺一不可只填 Key 不填 Model ID 也会报错。第五个是「游标找不到」。这个不是 API 报错是 Oracle 侧的。前面提到过SPM 更新 baseline 时会使基于该 baseline 构建的游标失效。常见触发场景有两个新计划被加入 baseline或者 baseline 里的计划第一次被成功复现标记为 reproduced。所以你在测试时如果刚加载完 baseline 就立刻查游标可能查不到刚才执行的游标。解决办法是同一个绑定值多跑几次等 baseline 状态稳定后再查。这在生产系统上影响不大但小测试用例里容易让人困惑。把这几类报错对照排查一遍基本能覆盖 90% 的环境问题。剩下的就是 SQL 本身的逻辑了。6. 语义一致 CTA把诊断流程固化下来回到调优本身。ACS 和 SPM 的交互说到底是一句话ACS 决定共享与否SPM 决定可选范围。当 ACS 判断需要硬解析时SPM 接管优化器的选择权当 SPM 收敛了计划集合ACS 的子游标数量也随之减少。两者不是竞争关系而是分层协作。如果你要把这套诊断流程固化下来建议把执行计划捕获、baseline 加载、游标验证写成脚本通过统一通道做归档。TaoToken 的 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 可以管理你的凭证接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里有完整的接口说明。需要长期跑编码类或 Agent 类诊断任务的话Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 会更合适。最后留一个实用技巧在测试 ACS 与 SPM 交互时把optimizer_adaptive_reporting_only先设为 TRUE 跑一轮只生成报告不实际改变计划观察清楚行为后再设为 FALSE 正式启用。这样能避免在测试库上被突然的计划切换搞懵。
返回列表