1. 从 23:58:39 到 00:00:12:exact 下那 1000 条 select 的 93 秒
cursor_sharing=exact 下,1000 条只差 object_id 的 select 全部硬解析,flush 之后要 93 秒;同一套脚本换成 force 或 similar 只要 2 秒。想把这段现象丢给 Codex 问清楚,又不想折腾官方 Key,可以走 TaoToken:打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 建一把 Key,Base URL 填 https://taotoken.net/api,模型 ID 以模型广场当时列表为准。TaoToken 在这条链路里只负责供 Key,它不连 Oracle,也没必要连——诊断 SQL 永远由你在本地 SQL*Plus 执行,再把结果贴回对话。
原文的实验条件写得很清楚:Oracle 12c,sys 用户登入 cdb$root,在 dba_objects 上连续跑 1000 条 object_id 从 1000 到 1999 的查询,每条之间只差一个数字。脚本先 flush shared_pool 和 buffer_cache,再 alter session set cursor_sharing='exact',然后从 23:58:39 一路拖到 00:00:12,93 秒;把 alter session 换成 force,00:14:49 开始、00:14:51 结束;similar 同样是 2 秒。这三个数字不是重点,重点是它们背后对应三种完全不同的游标共享条件,搞清楚这个,才能让 Codex 给你讲明白,而不是让它替你去改参数。
1.1 只差一个字面量,父游标为什么还是共享不了
在 exact 模式下,Oracle 判定两条 SQL 能不能共享游标,看的是 SQL 文本是否完全一致。select * from dba_objects where object_id=1000 和 object_id=1001 在字符层面不是同一条语句,所以它们各自生成一个父游标,各自经历一次硬解析。1000 条语句就是 1000 次硬解析,每次都要做语法分析、语义检查、生成执行计划,再把游标挂进 library cache。
这里容易被忽略的是「隐式游标共享」并不会因为 where 列相同、只差一个常量就生效。exact 的语义是字面量级别的一致,哪怕多一个空格、大小写不同,在游标匹配时都可能被视作不同文本。原文里那 1000 条语句的结构一模一样,唯一变量就是 object_id 的取值,在 exact 下它们的父游标几乎不可能互相复用。理解这一点,后面再看 force 和 similar 才有参照系。
1.2 flush shared_pool 之后,alter session 只影响当前会话
原文脚本第一段是 alter system flush shared_pool,第二段才是 alter session set cursor_sharing。这两个动作的层级不同:flush 作用于整个实例的共享池,把已有游标全部清掉;alter session 只改当前会话的参数值,其它会话不受影响。测试里之所以要 flush,是为了让 1000 条 select 从空池开始解析,把硬解析的代价完整暴露出来;如果不 flush,之前跑过的游标可能已经缓存,测出来的时间会被历史游标污染。
show parameter cursor_sharing 这一步不是走过场,它是在确认当前会话真的拿到了 exact。12c 的 CDB 环境里,参数可能来自 pdb 或根容器,直接看参数值比凭记忆可靠。做对照实验时,把每次 alter session 后的实际值截图留档,后面喂给 Codex 的上下文才完整——否则它会默认你在用默认参数,解释方向就容易跑偏。
1.3 force 和 similar 把字面量替换成绑定变量之后
force 做的事情是:不管 SQL 文本长什么样,Oracle 都会把 where 条件里的字面量自动替换成绑定变量,随后相同结构的语句就按绑定变量版本去共享游标。第一条 select object_id=1000 硬解析一次,并借助 bind peeking 拿到一个执行计划;从 1001 到 1999 都复用这个计划,走软解析。原文提到的「一次硬解析、多次软解析」正是这个机制的结果,这也是 1000 条语句能从 93 秒降到 2 秒的直接原因。
similar 在原文测试里表现和 force 一样,是因为条件分布均匀。它的规则更细:谓词条件被认为可能影响执行计划时会重新分析,否则才重用。均匀分布下每个 object_id 对应执行计划几乎一样,similar 不必为每个值生成新的子游标,于是耗时和 force 持平。一旦条件列分布偏斜,similar 会为新的绑定值做 bind peeking 并生成新的 child cursor,父游标下的子游标数量快速上升,遍历和 latch 的成本就会冒出来。12c 之后 Oracle 取消了 similar 的推荐地位,这条测试曲线正好是一个直观注脚。
2. Codex 接 TaoToken:config.toml 里只动几行
2.1 先在官网把 Key 建出来
Codex 本身不接受 Oracle 的账号体系,它需要一个 API Key 和一个兼容的 Base URL。打开 TaoToken 注册登录,进入控制台创建一把 Key,复制出来先放到本地环境变量里,不要直接写进 config.toml 的明文位置。Key 一律用 YOUR_API_KEY 占位,实际值只存在你自己的 shell 会话或密钥管理工具里,避免在截图、日志、对话记录里泄露。
创建 Key 的入口在控制台的 API Keys 页面,同一页还能看到当前 Key 的用量和状态。如果你打算长期用 Codex 做这种数据库排障类的问答,可以先看模型广场里哪些模型在你当前套餐内可用,再决定模型 ID 填哪个。原文里读者用到的是「把现象和 alter session 输出贴给 Codex」,这种任务对上下文长度有要求,Key 对应的通道能不能覆盖这个长度,值得在创建前先确认。
2.2 ~/.codex/config.toml 的 model_provider 段
Codex 读的是 ~/.codex/config.toml,配置结构是顶层 model 加一个 model_provider 指向具体供应商段。把供应商段的 base_url 指向 https://taotoken.net/api,末尾不要加 /v1,这是最容易写错也最容易排查的一个点。示例配置如下:
model = "YOUR_MODEL_ID" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY"env_key 写的是环境变量名,不是 Key 本身。配置完在 shell 里导出:
export TAOTOKEN_API_KEY="YOUR_API_KEY"env_key 和导出名必须一字不差,大小写敏感。保存后重开一个终端,让 Codex 重新读取配置和环境变量。
2.3 模型 ID 与 Key 别写死在会话里
model 字段填什么,不要凭记忆猜。以 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 模型广场当时列表为准,把可用的模型 ID 复制过来,避免写一个不存在的名字导致请求被拒。模型广场会随上架情况变化,昨天能用的 ID 今天不一定还在同一个套餐里,写配置前扫一眼比事后排 404 省事。
把 Key 放在环境变量而不是 config.toml 里,还有一个好处:换 Key 不用改配置文件,也不会因为误提交把密钥带进版本库。如果你在一台机器上同时跑多个 Codex 配置,用 shell 里临时 export 的方式更灵活。配置本身很短,真正需要花时间的是让 Codex 理解你要问的 Oracle 问题,所以别在 Key 和 URL 上来回折腾。
3. 把 exact/force/similar 现象整包丢给 Codex,让它核对游标共享条件
3.1 喂给 Codex 的上下文清单
想让 Codex 回答「为什么 FORCE 能压到 2 秒」,光丢一句结论没用,要把原文那组可比的数据给全:exact 下从 23:58:39 到 00:00:12 共 93 秒,force 下 00:14:49 到 00:14:51 共 2 秒,similar 同为 2 秒;测试对象是 12c cdb$root 中的 dba_objects;脚本先 flush shared_pool 和 buffer_cache,再 alter session set cursor_sharing='exact';1000 条语句只差 object_id,取值从 1000 到 1999。把这些事实按条列出来,比一句「exact 很慢」有用得多。
测试脚本本身也建议一并贴给 Codex,但注意不要直接复制原文的长段落,整理成下面这种可读形式即可:
alter system flush shared_pool; alter system flush buffer_cache; alter session set cursor_sharing = 'exact'; show parameter cursor_sharing; set termout off; set echo off; select * from dba_objects where object_id = 1000; select * from dba_objects where object_id = 1001; -- 省略中间若干条,直到 select * from dba_objects where object_id = 1999; set termout on; set echo on; select count(*) from dba_objects where object_id between 1000 and 1999;有了这些,Codex 才有可能把 93 秒和「1000 次硬解析」对应起来,而不是泛泛地说 exact 更安全、force 更快。
3.2 让 Codex 串起 bind peeking、child cursor 和直方图
可以这样向 Codex 提要求:请按 exact、force、similar 三种设置分别说明游标共享条件,并结合给出的执行时间解释差异来源;重点讲清楚 force 模式下 bind peeking 取到的执行计划如何成为唯一共享计划,similar 模式为什么会为不同绑定值生成 child cursor,以及谓词列上存在直方图时会发生什么变化。这样问,它会去核对概念,而不是只复述参数定义。
原文总结了 force 的几个关键行为:自动把 where 条件取值替换为绑定变量;第一次替换时依据 bind peeking 取到一个执行计划,形成子游标;之后只要父游标可共享,就强制复用这个子游标,不再判断是否最优;这种方式能限制一个父游标的 version_count,避免 library cache 里堆积大量硬解析。把这些要点交给 Codex 复核,让它指出哪些属于机制描述、哪些属于副作用,你就能分清哪些结论可以直接用在生产判断上。
similar 的讨论要落到两个风险:条件列取值少时子游标数量可控,取值连续或非常多时 child cursor 会膨胀,遍历和 latch/pin 成本上升;而且生成子游标的标准是绑定值是否相同,不是执行计划是否相同,分布均匀时会出现大量计划相同的子游标。原文还提到谓词列上没有直方图时,similar 的表现会接近 force。这几条都可以让 Codex 逐条确认,再决定你要不要在生产里碰 similar。
3.3 本地执行 v$sql 查询,把结果贴回对话
Codex 不会、也不应该直接连你的 Oracle 实例。正确的桥是:让 Codex 生成诊断 SQL,你在本地 SQL*Plus 里执行,把输出贴回对话,让它基于真实结果判断。比如想看 exact 跑完后每个字面量语句的解析情况,可以让它写一条针对 v$sql 的查询:
select sql_id, child_number, parse_calls, executions from v$sql where sql_text like 'select * from dba_objects where object_id%' order by sql_id, child_number;在 exact 场景下,你大概率会看到很多条 sql_id,每条对应极少量的 parse_calls 和 executions;换成 force 再跑一遍,sql_id 会收敛,child_number 数量也可能明显减少。把两组结果分别贴回给 Codex,让它对照解释,比你直接问「force 是不是更好」有价值得多。诊断 SQL 由你执行,结论由 Codex 分析,这条分工线要守住。
原文里换条件为 select * from dba_objects where object_id < xxxx 后,分布偏斜会放大 force 的 bind peeking 问题。验证时同样先让 Codex 生成针对 v$sql 和 v$sql_shared_cursor 的查询,你在本地跑,再把结果贴回去。若它建议直接在你的生产库上执行 impdp、FETCH 或类似动作,要当场纠正:这些操作必须由你在受控环境里手工做,AI 只负责解释和生成 SQL。
4. Codex 答非所问或连不上时的排障顺序
4.1 Base URL 尾部别带 /v1,env_key 名字要对上
配置完成后如果 Codex 报连不上,先看 base_url 有没有手滑写成 https://taotoken.net/api/v1。产品的接口地址是 https://taotoken.net/api,末尾不带 /v1,多这一截会导致路径拼接后请求打到不存在的路由。第二个常见点是 env_key 和实际导出的环境变量名不一致,比如 config.toml 里写 TAOTOKEN_API_KEY,shell 里导出的却是 TAOTOKEN_KEY,Codex 找不到 Key,请求自然失败。
第三类问题是模型 ID 写了列表里没有的名字,请求会被拒。排查顺序建议从 URL 到环境变量再到模型 ID,逐项对照,不要一次改三处。改完配置记得新开终端或重新 source 配置文件,Codex 通常只在启动时读取一次 ~/.codex/config.toml,老会话里不会自动刷新。
4.2 Codex 说“我直接连你的库跑一下”时怎么纠
这类模型的默认习惯是给出完整操作建议,有时会写成「连接数据库执行以下语句」。你要明确告诉它:你不会把生产库连接交给它,所有 SQL 由你本地执行;它只需要生成可复制的 SQL、解释输出、对照 exact/force/similar 的差异。把这条约束写进对话开头,后续回答会收敛到生成和解释,而不是指挥你去连库。
原文里那种 sys 登入 cdb$root 的测试,属于受控环境下的实验,不是让 AI 远程操作。复现时也一样:你在本地 SQL*Plus 里 alter system flush shared_pool 和 flush buffer_cache,注意这会影响整个实例的共享池,绝对不能在正在跑业务的生产库上随手执行。Codex 负责告诉你每条语句的作用和风险,执行决策留在你手里。
4.3 similar 在 12c 被拿掉,force 遇到偏斜列会翻车
原文最后给的选择逻辑值得让 Codex 再确认一遍:条件列分布均匀、类似 OLTP 的短查询,force 能显著减少硬解析;条件列分布偏斜,比如一万里挑十条和一万条里挑九千条共用同一个绑定变量,force 复用的第一个执行计划可能对后续取值并不合适;OLAP 系统更适合 exact,且通常不鼓励用绑定变量,因为解析成本相对执行成本几乎可以忽略,真正重要的是执行计划是否正确。12c 之后 similar 不再被推荐,这条也要写进你的判断清单。
让 Codex 把这些结论和你贴回去的 v$sql 结果对照,看是否存在「分布均匀但 child cursor 仍然很多」或「force 下解析次数没降下来」的反例。若出现反例,优先怀疑直方图、游标失效、绑定变量窥探时机,而不是直接改参数。参数改起来只要一行,副作用排查却可能要很久,这也是为什么先用 Codex 梳理机制、再决定是否动 session 级设置,比一上来就 alter system 稳妥。
5. 跑通之后回控制台看一眼这次调用
配置保存、Codex 能正常回答 Oracle 游标问题时,回到 TaoToken 模型对话 用同一把 Key 发一条测试消息,确认模型 ID 和 Base URL 都没填错。模型对话里返回正常,说明通道没问题,接下来再让 Codex 处理你贴过去的 v$sql 输出就有底了。若要长期做这类排障问答,可以打开 Coding Plan 看套餐是否够用,Key 的创建和轮换在 控制台 API Keys 管理。
文末再提醒一次原文的边界:exact、force、similar 的选择不是改一行 alter session 就完事,它牵扯执行计划稳定性、bind peeking、直方图和系统类型。Codex 能帮你把原文那组 93 秒与 2 秒的对照讲清楚,也能帮你生成验证用的 v$sql 查询,但 flush shared_pool 这类动作必须由你在确认过影响的本地或受控环境里执行。把 Key 配好、把问题问细,剩下的判断留在自己手里,这套流程才算真正可落地。