多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

统计没有使用绑定变量的SQL语句:用FORCE_MATCHING_SIGNATURE与EXACT_MATCHING_SIGNATURE定位硬解析源头

统计没有使用绑定变量的SQL语句:用FORCE_MATCHING_SIGNATURE与EXACT_MATCHING_SIGNATURE定位硬解析源头 1. 硬解析飙升时先搞清楚 FORCE_MATCHING_SIGNATURE 到底在比什么线上库突然 CPU 打满AWR 里硬解析每秒几百次shared pool 里堆着成千上万条长得几乎一样的 SQL——这是很多 DBA 都遇到过的场景。问题通常出在应用没走绑定变量每条 SQL 的字面量不同Oracle 就把它们当成完全不同的语句各生成一份执行计划。要定位这些语句v$sql里的FORCE_MATCHING_SIGNATURE和EXACT_MATCHING_SIGNATURE是最直接的两个抓手。先说清楚这两个字段是什么。EXACT_MATCHING_SIGNATURE是对 SQL 文本做精确哈希只要文本有一个字符不同包括 where 条件里的字面量值就不一样。FORCE_MATCHING_SIGNATURE则是在把字面量替换成绑定变量占位符之后再算哈希所以where id1、where id2、where id3这三条语句的 FORCE 值相同EXACT 值不同。这个差异就是判断是否使用绑定变量的核心依据当 FORCE 相同而 EXACT 不同时说明这些语句本可以共用执行计划却因为字面量差异被拆成了多条硬解析。适合谁看日常做 Oracle 性能优化的 DBA、负责 SQL 审核的开发者、以及需要给应用团队提优化建议的运维。你不需要是内核专家只要能连上库、能查v$sql就能跟着下面的步骤把硬解析源头筛出来。整篇会交付可直接复制的查询 SQL、cursor_sharing参数的验证方法以及如何用 TaoToken 统一管理这些诊断脚本的调用通道避免脚本散落在各台机器上、Key 到处复制。需要提醒的是FORCE_MATCHING_SIGNATURE只反映文本层面能否合并不代表这些语句一定该合并。有些字面量差异会影响执行计划选择比如分区键、数据倾斜列强行合并反而可能让某条语句用错计划。所以筛出来只是第一步后面还要结合执行计划和业务语义判断。2. TaoToken 前置把诊断脚本的模型调用统一到一个 Key做 SQL 硬解析排查时除了手工查v$sql很多人会用脚本自动分析、让模型帮忙解读执行计划或生成优化建议。脚本一多问题就来了每台跳板机、每个同事本地都存一份 API Key模型 ID 写法还不一致换个人跑就报 401。TaoToken 在这里的作用是提供一个统一的 API 通道把 Key 和模型配置集中管理脚本里只引用一个 Base URL 和一个 Key。TaoToken 是一个大模型 API 聚合网关兼容 OpenAI 风格的接口协议。你可以把它理解成一个统一入口不管底层调的是哪个模型脚本里写的都是同一套base_urlapi_keymodel三件套。对 DBA 来说好处是诊断脚本可以跨机器复用不用每台机器重新配一遍凭证。接入前先拿到凭证。打开官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册后进入控制台创建 API Key控制台入口https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewriteAPI Key 管理https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite创建好 Key 之后API 的基础地址是https://taotoken.net/api注意这个地址不加 UTM 参数直接用于代码里的base_url。模型 ID 在文档里能查到常用的对话模型和代码模型都有对应标识接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite模型对话体验https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite如果你打算长期跑 SQL 分析类的 Agent 或定时脚本可以看下 Coding Plan它更适合高频、持续的调用场景Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite这里要强调一点TaoToken 只是模型调用的通道不碰你的数据库。诊断 SQL 还是在本地或跳板机上执行模型只负责解读你贴过去的执行计划文本。不要把生产库连接串、账号密码发给模型这是基本纪律。3. 可复制配置cursor_sharing 验证与硬解析筛查脚本这一节给两段能直接用的东西一段是验证cursor_sharing行为的实验一段是筛查未使用绑定变量语句的查询。先做实验建立直觉再上生产查询。3.1 建测试表并观察签名变化先建一张小表插入几条数据用来复现字面量差异create table tb_force(id integer, name varchar2(10)); insert into tb_force(id, name) values (1,111); insert into tb_force(id, name) values (2,222); insert into tb_force(id, name) values (3,333); commit;确认当前cursor_sharing的取值show parameter cursor_sharing;默认情况下会看到VALUE是EXACT。在这个模式下连续执行三条只有 id 不同的语句select /*mystatement*/ * from tb_force where id1; select /*mystatement*/ * from tb_force where id2; select /*mystatement*/ * from tb_force where id3;然后查v$sql对比两个签名字段select sql_text, FORCE_MATCHING_SIGNATURE, EXACT_MATCHING_SIGNATURE from v$sql t where sql_text like %mystatement%;你会看到三条记录的FORCE_MATCHING_SIGNATURE完全相同而EXACT_MATCHING_SIGNATURE各不相同。这说明在cursor_sharingEXACT下这三条语句各自硬解析、各占一份 shared pool 空间执行计划无法共用。接着把参数改成 FORCE清空 shared pool 后重跑alter system set cursor_sharingforce; alter system flush shared_pool;再执行那三条查询然后查v$sql会发现只剩一条记录SQL 文本里的字面量被自动替换成了:SYS_B_0两个签名字段的值也变得一致。这就是cursor_sharingforce的效果系统强制把相似语句合并自动绑定变量。cursor_sharing三个取值的区别用一张表对照更清楚取值行为是否推荐EXACT只有文本完全相同的语句才共用游标默认值推荐配合应用层绑定变量SIMILAR字面量不同但结构相同的语句尝试共用除非字面量影响语义或计划不推荐历史版本 bug 较多FORCE强制把字面量替换为绑定变量后共用游标谨慎可能影响执行计划选择3.2 筛查未使用绑定变量的语句理解了签名差异就能反推FORCE 相同但 EXACT 不同的语句就是没走绑定变量的候选。下面这条查询按 FORCE 签名分组统计每组有多少条不同的 EXACT 签名数量越多说明字面量差异越严重select to_char(FORCE_MATCHING_SIGNATURE) as FORCE_MATCHING_SIGNATURE, count(1) as counts from v$sql where FORCE_MATCHING_SIGNATURE 0 and FORCE_MATCHING_SIGNATURE EXACT_MATCHING_SIGNATURE group by FORCE_MATCHING_SIGNATURE having count(1) a order by 2 desc;a是阈值比如填 20就只列出硬解析超过 20 次的签名组。拿到某个FORCE_MATCHING_SIGNATURE值后再展开看具体是哪些语句select t.* from v$sql t where FORCE_MATCHING_SIGNATURE 16456394970215993993;把上面那个数字换成你实际查出来的值即可。展开后重点看SQL_TEXT、EXECUTIONS、ELAPSED_TIME、PARSE_CALLS这几列PARSE_CALLS高、EXECUTIONS相对低说明反复解析、复用差。3.3 用 TaoToken 统一脚本调用配置如果你把上面的查询封装成 Python 脚本让模型自动解读结果配置可以写成这样OpenAI 兼容风格{ base_url: https://taotoken.net/api, api_key: sk-你的TaoToken密钥, model: 你在文档中选定的模型ID, timeout: 60 }对应的 Python 调用片段from openai import OpenAI client OpenAI( base_urlhttps://taotoken.net/api, api_keysk-你的TaoToken密钥 ) resp client.chat.completions.create( model你在文档中选定的模型ID, messages[ {role: user, content: 以下是 Oracle v$sql 查询结果请帮我判断哪些语句未使用绑定变量\n sql_result_text} ] ) print(resp.choices[0].message.content)把base_url、api_key、model三件套写进配置文件脚本里只读配置换机器时改一处即可。这样团队里每个人跑同一份诊断脚本用的都是同一个通道不会再出现我这边能跑你那边 401的情况。4. 验证请求从查询结果到成功定位硬解析源头配置和 SQL 都就位后跑一遍完整流程验证。假设你在测试库上已经用 3.1 的实验制造了字面量差异现在执行 3.2 的分组查询阈值设成 1select to_char(FORCE_MATCHING_SIGNATURE) as FORCE_MATCHING_SIGNATURE, count(1) as counts from v$sql where FORCE_MATCHING_SIGNATURE 0 and FORCE_MATCHING_SIGNATURE EXACT_MATCHING_SIGNATURE group by FORCE_MATCHING_SIGNATURE having count(1) 1 order by 2 desc;预期结果里会出现一行COUNTS是 3对应 id1、2、3 三条FORCE_MATCHING_SIGNATURE是一个科学计数法表示的大数。把这个值复制出来代入展开查询select sql_text, executions, parse_calls, elapsed_time from v$sql where FORCE_MATCHING_SIGNATURE 1.30958144015941E19;注意这里有个坑FORCE_MATCHING_SIGNATURE在v$sql里是 NUMBER 类型但显示成科学计数法。直接拿科学计数法的字符串去等值匹配Oracle 能隐式转换但更稳妥的写法是用to_char统一格式或者用to_number转换。如果查不到结果先确认你复制的值和库里的精度是否一致。成功的结果应该是展开后看到三条SQL_TEXT只有 where 条件不同PARSE_CALLS都是 1 或更高说明每条都独立解析过。这就定位到了硬解析源头。接下来把SQL_TEXT里的表名、字段名提取出来反馈给应用团队让他们改成绑定变量写法比如把where id1改成where id:id。如果你用 TaoToken 脚本自动分析把展开查询的结果贴给模型让它输出哪些语句建议改绑定变量、哪些因为数据倾斜不建议合并的判断。请求成功的标志是模型返回了结构化的分析文本而不是报错。如果返回的是空内容或超时先检查base_url是否写成了带 UTM 的地址——代码里应该用https://taotoken.net/api不带任何查询参数。再补一个验证点改完应用代码后重新查分组 SQL对应的FORCE_MATCHING_SIGNATURE组应该消失或者COUNTS降到 1。这说明字面量差异消除了硬解析降下来了。这个前后对比就是优化效果的量化证据。5. 常见报错排查401、local proxy failed 与签名查不到排查过程中最容易卡在几个具体报错上逐个说清楚。报错一401 Unauthorized。脚本调用 TaoToken 时返回 401通常是 Key 没配对或复制时带了空格。检查api_key字段是不是完整的sk-开头字符串前后有没有多余换行。另外确认base_url写的是https://taotoken.net/api如果误写成带/v1或其他路径也可能导致鉴权失败。Key 是在控制台创建的如果刚删过旧 Key记得同步更新所有脚本里的配置。报错二local proxy failed 或连接超时。这类报错一般是本机网络环境或代理设置导致的。先确认本机能正常访问https://taotoken.net/api可以用curl测一下连通性。如果公司网络有出口限制联系网络管理员放行。注意不要用任何非正规的网络工具去绕过限制合规访问是前提。脚本里的timeout设成 60 秒比较稳妥太短容易在模型返回长文本时被截断。报错三查询 v$sql 返回空或 FORCE_MATCHING_SIGNATURE 查不到。几种可能一是 shared pool 刚被 flush 过语句还没重新解析等业务跑一会儿再查二是权限不够普通用户看不到v$sql的全部内容需要SELECT ANY DICTIONARY或 DBA 角色三是FORCE_MATCHING_SIGNATURE的值精度问题用to_char转换后再比对。还有一种情况是语句确实都用了绑定变量那查不到反而是好事。报错四reading choices 相关报错。如果你用流式接口偶尔会看到解析choices字段失败。这通常是返回体被截断或格式异常检查timeout和网络稳定性重试一次一般能恢复。如果持续出现把stream关掉用非流式请求验证。报错五OAuth 或鉴权方式混淆。TaoToken 用的是 API Key 鉴权不是 OAuth 流程。如果你在代码里套了 OAuth 的 token 获取逻辑会一直失败。直接传api_key即可。Claude Code 这类工具接入时也是填 Base URL Key Model ID 三件套不要走 OAuth 授权码那套。排查顺序建议先确认网络能通再确认 Key 有效然后确认base_url和model写对最后才怀疑数据库侧。大部分问题出在前三步。6. 把诊断脚本沉淀成可复用的通道硬解析排查不是一次性的活。今天定位了一批没走绑定变量的语句过两周新功能上线可能又冒出来一批。与其每次手工敲 SQL、临时配 Key不如把 3.2 的查询和 TaoToken 调用封装成一个固定脚本放进团队的运维工具箱。具体做法把分组查询和展开查询写成两个.sql文件用 shell 或 Python 包一层输出结果自动发给 TaoToken 的模型做初步分类。配置文件里只保留base_url、api_key、model三个字段Key 从环境变量读取不硬编码进脚本。这样换人、换机器改环境变量就行。模型选择上日常解读执行计划用对话模型就够如果要做复杂的 SQL 改写建议可以切到代码能力更强的模型。模型 ID 在文档里查别凭记忆写。长期高频跑的话Coding Plan 的额度模型更划算适合挂成定时任务。最后留一个实用习惯每次优化完把优化前后的FORCE_MATCHING_SIGNATURE分组数量记下来形成趋势。数字往下走说明绑定变量推广到位数字反弹说明有新代码没遵守规范。这比看单次 AWR 报告更能反映长期治理效果。
返回列表