ARTICLE DETAIL

资讯详情

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

Oracle Cardinality Feedback 实战:用 TaoToken 统一 Key 复现执行计划漂移与修复

Oracle Cardinality Feedback 实战:用 TaoToken 统一 Key 复现执行计划漂移与修复 1. 执行计划为什么总在第二次执行时变脸如果你在 Oracle 里遇到过这种场景同一条 SQL第一次跑得挺快第二次突然换了执行计划第三次又换回来AWR 里看 SQL_ID 没变但 Plan Hash Value 一直在跳那大概率是 Cardinality Feedback基数反馈简称 CFB在背后动手。Cardinality Feedback 是 CBO 的一个自适应机制。当优化器发现某条 SQL 的估算行数E-Rows和实际行数A-Rows差距过大时它会把这个实际行数记下来下次执行时用这个修正过的基数重新硬解析生成新的执行计划。听起来很智能但问题在于如果这个反馈本身不稳定或者表数据分布倾斜严重就会导致计划反复漂移DBA 排查起来非常头疼。这个机制主要在这几种情况下被触发表没有统计信息且动态采样也没开查询条件涉及多列但没有收集扩展统计信息查询条件里带函数导致 CBO 无法准确估算。Oracle 的处理流程是第一次执行时监控 A-Rows 和 E-Rows差距大就标记并记录实际行数下次执行时硬解析重新生成计划如果差距不大就不再监控。本文面向 DBA 和 SQL 调优人员给出一套可复制的排查流程从初始化参数设置、监控脚本、到通过 TaoToken 统一 Key 调用诊断接口做计划对比验证把反馈触发到计划稳定这个闭环走完。适合谁看正在被执行计划漂移折磨、想搞清楚 CFB 到底怎么工作的 Oracle 从业者。2. TaoToken 前置准备统一 Key 与诊断接口调用在开始排查之前先说一下为什么这里要引入 TaoToken。排查 CFB 问题时我们经常需要调用大模型接口来做执行计划的语义对比、SQL 改写建议、或者把 AWR 报告里的关键指标丢给模型分析。如果每个工具、每个脚本都单独配一套 Key管理起来很乱。TaoToken 的作用就是提供一个统一的 API 入口一个 Key 走通模型对话、诊断接口和编码辅助。你需要先拿到 Key。访问官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册后进入控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 创建 API Key。然后在 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 复制你的 Key格式通常是 sk- 开头的一串字符。这里要强调三件套的概念Base URL、Key、Model ID。不管你用的是 Cline、Codex 还是自己写的 Python 脚本这三个东西必须配对。Base URL 统一用 https://taotoken.net/api注意这个地址不加 UTM 参数Key 就是你刚创建的那串Model ID 根据你实际要调用的模型填比如 claude-sonnet-4-20250514 或者 gpt-4o 这类。如果你用的是 Claude Code 做 SQL 脚本的润色和注释生成可以在 settings 里配置。配置文件路径通常在 ~/.claude/settings.json内容如下{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的Key, ANTHROPIC_MODEL: claude-sonnet-4-20250514 } }如果你用的是 Cline 或者 Roo Code 这类 VS Code 插件在 MCP 配置里填 Base URL 和 Key 即可。Codex 用户则在 auth.json 里配置{ base_url: https://taotoken.net/api, api_key: sk-你的Key, model: gpt-4o }配好之后你可以先用模型对话接口 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 测试一下 Key 是否可用。发一句你好过去能正常返回就说明通了。这一步别跳过后面排查 CFB 时如果接口调不通你会分不清是 Oracle 的问题还是 Key 的问题。对于长期做 SQL 调优和 Agent 自动化的场景可以考虑 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite额度更划算。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite里面有各语言的调用示例。3. 可复制配置初始化参数与监控脚本排查 CFB 的第一步是控制变量。你需要先确认动态采样是否开启因为动态采样和 CFB 会互相干扰。如果动态采样开着CBO 可能直接用采样结果CFB 就不一定触发如果关掉动态采样CFB 的行为会更明显。在会话级别关闭动态采样alter session set optimizer_dynamic_sampling0;然后确认当前会话的 CFB 相关参数。Oracle 没有直接叫 cardinality_feedback 的参数CFB 是优化器内部行为但你可以通过 10053 trace 或者 V$SQL_SHARED_CURSOR 来观察。先查一下有没有子游标正在使用反馈统计select sql_id, use_feedback_stats from v$sql_shared_cursor where use_feedback_stats Y;如果返回了 SQL_ID说明这条 SQL 已经触发了 CFB。你可以进一步查这个 SQL 的估算行数和实际行数对比select sql_id, child_number, plan_hash_value, executions, rows_processed, optimizer_cost, cpu_cost, io_cost from v$sql where sql_id 你的SQL_ID order by child_number;这里 rows_processed 是实际处理行数optimizer_cost 是优化器估算成本。如果同一个 SQL_ID 有多个 child_number且 plan_hash_value 不同说明发生了多次硬解析计划在漂移。为了更直观地看 E-Rows 和 A-Rows 的差距可以用 DBMS_XPLAN 看执行计划select * from table(dbms_xplan.display_cursor(你的SQL_ID, null, ALLSTATS LAST));输出里 E-Rows 是估算A-Rows 是实际。如果第一次执行 E-Rows1014、A-Rows1差距巨大第二次执行 E-Rows 接近 A-Rows那就说明 CFB 生效了。如果你想用 TaoToken 的接口来做计划对比可以写一个 Python 脚本把两次执行的计划文本发给模型让它帮你分析差异点。配置如下import requests API_URL https://taotoken.net/api/v1/chat/completions API_KEY sk-你的Key headers { Authorization: fBearer {API_KEY}, Content-Type: application/json } payload { model: claude-sonnet-4-20250514, messages: [ {role: user, content: 对比以下两个执行计划指出基数估算差异和可能的修复方向\n计划1...\n计划2...} ] } resp requests.post(API_URL, headersheaders, jsonpayload) print(resp.json()[choices][0][message][content])这个脚本的关键是 Base URL 用 https://taotoken.net/api路径拼 /v1/chat/completions。Key 放在 Authorization 头里。Model ID 按你实际用的填。另外如果你想把 CFB 的监控做成自动化可以创建一个定时任务每隔几分钟查一次 V$SQL_SHARED_CURSOR发现有新的 USE_FEEDBACK_STATSY 就记录下来并通过 TaoToken 接口发一条分析请求。这样你就能在计划漂移的早期就发现问题。4. 验证请求与成功结果一次完整的计划对比现在我们来走一遍完整的验证流程。假设你有一条 SQLSQL_ID 是 0mp85ftwxaky5第一次执行时 E-Rows 和 A-Rows 差距很大第二次执行时 CFB 生效计划变了。第一步关闭动态采样清空共享池里这条 SQL 的游标alter session set optimizer_dynamic_sampling0; alter system flush shared_pool;注意 flush shared_pool 在生产环境要谨慎这里只是演示。实际排查时可以用 alter system flush buffer_cache 或者直接换一个 SQL_ID 来测试。第二步第一次执行 SQL然后查执行计划select * from table(dbms_xplan.display_cursor(0mp85ftwxaky5, null, ALLSTATS LAST));你会看到类似这样的输出| Id | Operation | Name | E-Rows | A-Rows | | 0 | SELECT STATEMENT | | | 1 | | 1 | TABLE ACCESS FULL| T1 | 1014 | 1 |E-Rows1014A-Rows1差距 1000 倍。第三步第二次执行同一条 SQL再查计划select * from table(dbms_xplan.display_cursor(0mp85ftwxaky5, null, ALLSTATS LAST));这次输出可能变成| Id | Operation | Name | E-Rows | A-Rows | | 0 | SELECT STATEMENT | | | 1 | | 1 | INDEX RANGE SCAN | IDX_T1| 1 | 1 |E-Rows 变成了 1和 A-Rows 一致说明 CFB 已经用上次的实际行数修正了估算计划从全表扫描变成了索引范围扫描。第四步确认 CFB 确实生效了select sql_id, use_feedback_stats from v$sql_shared_cursor where sql_id 0mp85ftwxaky5;如果 USE_FEEDBACK_STATSY说明这条 SQL 正在使用反馈统计。第五步用 TaoToken 接口做一次计划对比。把两次的计划文本整理好发给模型import requests API_URL https://taotoken.net/api/v1/chat/completions API_KEY sk-你的Key plan1 | Id | Operation | Name | E-Rows | A-Rows | | 1 | TABLE ACCESS FULL| T1 | 1014 | 1 | plan2 | Id | Operation | Name | E-Rows | A-Rows | | 1 | INDEX RANGE SCAN | IDX_T1| 1 | 1 | prompt f对比以下两个 Oracle 执行计划分析 Cardinality Feedback 的作用并给出稳定计划的建议\n计划1{plan1}\n计划2{plan2} headers { Authorization: fBearer {API_KEY}, Content-Type: application/json } payload { model: claude-sonnet-4-20250514, messages: [{role: user, content: prompt}] } resp requests.post(API_URL, headersheaders, jsonpayload) result resp.json() print(result[choices][0][message][content])如果接口返回正常你会得到一段分析指出第一次全表扫描是因为 CBO 高估了基数第二次索引扫描是因为 CFB 修正了基数。模型可能还会建议你收集统计信息或者创建扩展统计来从根本上解决问题。成功的结果标志是V$SQL_SHARED_CURSOR 里 USE_FEEDBACK_STATSY执行计划从全表扫描变成索引扫描E-Rows 和 A-Rows 趋于一致TaoToken 接口返回的分析结果符合预期。5. 常见报错排查401、local proxy failed 与 OAuth排查 CFB 的过程中你可能会遇到几类报错。这里按真实场景列一下。第一类TaoToken 接口返回 401 Unauthorized。这个最常见原因是 Key 没填对或者过期了。检查你的 Authorization 头是不是 Bearer sk-xxx 格式Key 有没有多余空格。如果你用的是 Claude Code检查 settings.json 里的 ANTHROPIC_API_KEY 是不是正确。401 的本质是认证失败跟 Oracle 没关系先把 Key 问题解决。第二类local proxy failed 或者 connection refused。这个通常出现在你本地配了代理但代理没启动或者 Base URL 写错了。TaoToken 的 Base URL 是 https://taotoken.net/api不要写成 https://taotoken.net/api/v1 再加 /v1会重复。如果你在 Codex 的 auth.json 里配置确认 base_url 字段没有多余路径。这个报错跟网络环境有关检查你的网络是否能正常访问 https://taotoken.net/api。第三类reading choices 报错比如 list index out of range 或者 choices is empty。这个说明接口返回了但结构不对可能是 Model ID 填错了或者请求体格式不对。检查你的 payload 里 model 字段是不是有效的模型名messages 是不是数组格式。如果返回体里没有 choices打印完整的 resp.json() 看看错误信息。第四类OAuth 相关报错。如果你用的是 Claude Code 的 OAuth 登录方式而不是 API Key可能会遇到 token 过期。这时候切回 API Key 方式在 settings.json 里显式配置 ANTHROPIC_API_KEY 和 ANTHROPIC_BASE_URL。OAuth 和 API Key 不要混用选一种。第五类Oracle 侧的报错。比如 ORA-00942 表或视图不存在检查你是不是用错了用户V$SQL_SHARED_CURSOR 需要 SELECT 权限。ORA-01031 权限不足需要 DBA 角色或者显式授权。ORA-00600 内部错误这个跟 CFB 可能有关查 alert log 和 trace 文件。第六类执行计划没变化。你关了动态采样执行了两次 SQL但计划还是全表扫描。可能原因是SQL 文本有细微差异导致 SQL_ID 不同或者 CFB 没有触发因为 E-Rows 和 A-Rows 差距不够大或者统计信息太旧导致 CBO 根本不走索引。这时候查 V$SQL_SHARED_CURSOR 确认 USE_FEEDBACK_STATS 是否为 Y如果不是说明 CFB 没生效需要检查触发条件。排查顺序建议先确认 TaoToken 接口通不通用模型对话页面发个测试消息再确认 Oracle 侧 SQL_ID 和计划变化最后对比两边的时间线。别一上来就怀疑 Oracle很多时候是 Key 配错了。6. 从反馈触发到计划稳定的闭环把上面的步骤串起来一个完整的闭环是这样的你先关闭动态采样清空游标执行 SQL 并记录第一次的 E-Rows 和 A-Rows。然后第二次执行观察计划是否变化查 V$SQL_SHARED_CURSOR 确认 CFB 生效。接着用 TaoToken 接口把两次计划发给模型做对比分析得到修复建议。最后根据建议收集统计信息或者创建扩展统计让 CBO 从根本上能估算准确不再依赖 CFB。这里的关键点是CFB 本身不是问题它是 Oracle 的自适应机制。问题在于当 CFB 反复触发时说明统计信息或者数据分布有问题。你的目标不是关掉 CFB而是让 CBO 能准确估算这样 CFB 就不会频繁介入。如果你需要长期监控 CFB 行为可以写一个定时脚本每隔一段时间查一次 V$SQL_SHARED_CURSOR把 USE_FEEDBACK_STATSY 的 SQL_ID 记录下来并通过 TaoToken 的接口做批量分析。接口地址用 https://taotoken.net/apiKey 用你创建的那串Model ID 按需选择。对于需要长期跑自动化任务的场景Coding Plan 的额度更合适。最后给一个实用技巧在收集统计信息时对于多列查询条件记得收集扩展统计信息extended statistics这样 CBO 能更准确地估算组合条件的基数从源头上减少 CFB 的触发。具体命令是select dbms_stats.create_extended_stats(SCHEMA_NAME, TABLE_NAME, (COL1, COL2)) from dual;执行完后重新收集表统计信息再跑一次 SQL观察 E-Rows 和 A-Rows 是否接近。如果接近了说明问题解决了。如果还是差距大可能需要考虑 SQL 改写或者加 hint。整个流程走下来你应该能从计划为什么变到怎么让它不变有一个清晰的路径。CFB 不可怕可怕的是不知道它在干什么。
返回列表