
1. ORA-01000 报错现场游标数到底被谁吃满了应用日志里突然刷出ORA-01000: maximum open cursors exceeded接口开始批量超时重启服务能好一阵过几个小时又复发。这个场景在 Oracle 上太常见了尤其是那种 REST 接口层写得不严谨、每次请求都prepareStatement却忘了close()的服务。你看到的报错是数据库抛的但根因往往在应用代码或者连接池配置里。先说清楚游标是什么。你可以把游标理解成数据库给每条 SQL 开的一个「取数窗口」会话执行一次查询、一次 DMLOracle 就会在会话级别分配一个游标句柄。open_cursors这个参数限制的是单个会话同时能打开的游标数量不是整个库的总量。默认值在不少环境里只有 50 或 300一旦某个会话里游标没被及时释放累积到上限下一条 SQL 就直接报 ORA-01000。这里有个容易混淆的点open_cursors是会话级上限而session_cached_cursors是会话缓存已关闭游标的数量两者不是一回事。很多人一看到报错就去调session_cached_cursors方向就偏了。真正要盯的是「当前会话打开了多少游标」以及「这些游标为什么没关」。排查思路分两条线。第一条线是连接池连接池把物理连接复用给不同请求如果池子里的连接被某个慢查询或未关闭的游标占住游标数会随着复用不断叠加。第二条线是游标泄漏代码里ResultSet、Statement、CallableStatement没有在finally里关闭或者 MyBatis 的SqlSession没 commit/close都会让游标一直挂着。我试过在一个 Spring Boot Druid 的项目里定位这类问题最后发现是某个导出接口用了CallableStatement调存储过程异常分支里漏了close()。所以别急着改数据库参数先把「谁在占游标」查出来再决定是调参还是改代码。下面会结合 TaoToken 统一 Key 通道把从定位到调优的完整链路走一遍包括可复制的查询语句、连接池配置片段和验证步骤。2. TaoToken 统一 Key 通道前置准备把排查工具接进来排查 Oracle 游标问题除了数据库本身的v$视图很多时候还需要借助 AI 辅助分析慢 SQL、生成排查脚本、解读执行计划。TaoToken 在这里的角色是提供一个统一的 API 通道让你用同一个 Key 就能调用多种模型能力不用在多个平台之间来回切换 Key 和 Base URL。官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。为什么排查数据库问题会用到模型通道举个实际场景你从v$open_cursor里捞出一堆sql_text里面有大量动态拼接的 SQL肉眼很难快速判断哪条是泄漏源。这时候把 SQL 文本丢给模型做归类、提取绑定变量模式效率会高很多。另外像 ORA-01000 这种报错模型可以帮你快速生成对应的排查 SQL 和连接池参数建议省去翻文档的时间。接入前你需要准备三样东西Base URL、API Key、Model ID。Base URL 统一填https://taotoken.net/api注意这里不加任何查询参数。API Key 在控制台的 API Keys 页面创建地址是 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。Model ID 根据你要用的模型填比如做代码分析可以选擅长代码的模型做文本归类选通用对话模型即可。如果你用的是 Claude Code 这类编码工具TaoToken 也提供了对应的接入方式文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。对于长期要做数据库排查、写脚本的开发者Coding Plan 会更划算入口是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。需要强调的是TaoToken 只是模型调用的通道它不碰你的数据库也不替代任何数据库客户端。你的 Oracle 连接、SQL 执行还是在本地或服务器上用 sqlplus、DBeaver、JDBC 完成。模型通道的作用是辅助你分析、生成脚本、解读结果。把这两件事分清楚后面的配置才不会乱。准备好 Key 之后建议先用模型对话页面做一次连通性验证地址是 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。随便问一句「Oracle ORA-01000 怎么排查」能正常返回就说明通道通了。这一步别跳过后面写脚本调用 API 时如果报 401你至少能确定是 Key 问题还是代码问题。3. 可复制配置open_cursors 查询调整与连接池参数片段这一节直接给可复制的内容。先看数据库侧。查询当前open_cursors设置值show parameter open_cursors;或者用SELECT name, value, isdefault FROM v$parameter WHERE name open_cursors;如果value只有一两百那确实偏小。一般业务库建议设到 1000高并发接口多的可以到 2000 甚至 3000但不要盲目拉太大因为每个游标都占 PGA 内存。调整语句ALTER SYSTEM SET open_cursors 1000 SCOPE BOTH;SCOPE BOTH表示同时改内存和 spfile重启后依然生效。改完再show parameter open_cursors确认一下。接下来查当前游标占用总量SELECT count(*) AS total_open_cursors FROM v$open_cursor;这个数字是实例级别的只能作为参考。真正要定位的是「哪个会话占得多」SELECT s.sid, s.serial#, s.username, a.value AS open_cursor_count FROM v$sesstat a, v$statname b, v$session s WHERE a.statistic# b.statistic# AND s.sid a.sid AND b.name opened cursors current AND s.username IS NOT NULL ORDER BY a.value DESC;拿到占用最高的sid之后查这个会话具体打开了哪些 SQLSELECT sid, sql_text, user_name, count(*) AS open_cursors FROM v$open_cursor WHERE sid IN (sid) GROUP BY sid, sql_text, user_name ORDER BY open_cursors DESC;把sid换成上一步查到的值。如果某条sql_text的open_cursors计数特别高基本就是泄漏点。连接池侧以 Druid 为例关键配置片段如下spring: datasource: druid: url: jdbc:oracle:thin://127.0.0.1:1521/ORCLPDB1 username: app_user password: your_password initial-size: 5 min-idle: 5 max-active: 20 max-wait: 60000 validation-query: SELECT 1 FROM DUAL test-while-idle: true test-on-borrow: false test-on-return: false remove-abandoned: true remove-abandoned-timeout: 300 log-abandoned: trueremove-abandoned和log-abandoned这两个参数在排查游标泄漏时特别有用前者会把超时未归还的连接强制回收后者会打印堆栈直接告诉你哪个代码路径借了连接没还。如果你用的是 HikariCP对应片段spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000 leak-detection-threshold: 60000leak-detection-threshold设成 60000 毫秒连接被借出超过 60 秒没归还就会打警告日志配合堆栈能快速定位泄漏代码。TaoToken 侧的配置片段以调用模型分析 SQL 为例用 curl 验证curl -X POST https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -d { model: your-model-id, messages: [ {role: user, content: 帮我分析这条 Oracle SQL 是否存在游标泄漏风险SELECT * FROM orders WHERE status ?} ] }把$TAOTOKEN_API_KEY换成你在控制台创建的 Keyyour-model-id换成实际模型 ID。这个片段可以直接贴到终端跑返回正常说明通道可用。4. 验证请求与成功结果复现游标增长并确认调优生效配置改完不能就算完得验证。验证分两步先复现游标增长再确认调整后不再触顶。复现的思路是人为制造一个游标泄漏场景。写一段 Java 代码循环执行查询但不关闭Statementpublic void leakCursors(Connection conn, int times) throws SQLException { for (int i 0; i times; i) { Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(SELECT * FROM orders WHERE rownum 10); rs.next(); // 故意不关闭 rs 和 stmt } }在open_cursors还是默认小值比如 300的环境里循环 400 次左右就会触发 ORA-01000。这时候去查v$sesstat能看到该会话的opened cursors current接近上限。然后把open_cursors调到 1000重启应用连接池再跑同样的循环。这次 400 次不会报错但查v$sesstat会发现游标数依然在涨只是没到顶。这说明参数调大只是缓解泄漏还在。真正的修复是在finally里关闭资源public void safeQuery(Connection conn) throws SQLException { try (Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(SELECT * FROM orders WHERE rownum 10)) { while (rs.next()) { // 处理结果 } } }用 try-with-resources 之后再跑循环opened cursors current会稳定在一个很小的值不再累积。验证 TaoToken 通道是否正常除了前面的 curl还可以在模型对话页面直接提问比如把v$open_cursor的查询结果贴进去让模型帮你归类哪些 SQL 属于同一类泄漏模式。成功返回的标志是模型能正确识别出重复的 SQL 模板并给出关闭建议。一个完整的验证闭环是这样的调大open_cursors→ 复现泄漏 → 确认报错消失但游标仍增长 → 修复代码 → 确认游标数稳定 → 用模型辅助复查 SQL 模式。每一步都有可观测的结果不是拍脑袋。5. 本篇常见错排查401、local proxy failed、reading choices 与 OAuth排查过程中会遇到几类典型报错逐个说。401 Unauthorized。调用 TaoToken API 时返回 401九成是 Key 问题。先确认Authorization头格式是Bearer key中间有空格。再确认 Key 没有多余换行或引号。如果 Key 是从控制台复制的注意别把前后空格带进去。还有一种情况是 Key 被删除或过期去 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 重新创建一个即可。local proxy failed。这个报错通常出现在本地工具比如某些 IDE 插件或 CLI配置了代理但代理不可用时。检查你的环境变量HTTP_PROXY、HTTPS_PROXY是否指向了一个已经关闭的本地端口。把这两个变量清掉或者确认代理服务在运行。注意这里说的是本地开发环境的网络配置问题跟数据库连接无关。reading choices 报错。调用模型接口后返回体解析失败提示读取choices字段出错。一般是响应不是预期的 JSON 结构可能因为请求体格式不对或者模型 ID 填错导致返回了错误信息。先用 curl 发一个最小请求体确认返回结构里有choices数组。如果返回的是{error: ...}那就是请求本身有问题先解决请求再解析。OAuth 相关报错。如果你用的是 Claude Code 或类似工具接入时可能遇到 OAuth 流程问题。这类工具通常需要配置 Base URL 和 Key而不是走 OAuth 登录。确认你填的是https://taotoken.net/api作为 Base URLKey 填在对应字段。如果工具强制走 OAuth检查是否有「使用 API Key」的选项。文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里有各工具的接入说明。ORA-01000 调参后仍报错。如果open_cursors已经调到 1000 甚至更大还是报 ORA-01000那基本可以确定是游标泄漏而且泄漏速度很快。这时候回到第 3 节的v$open_cursor查询把占用最高的sid和sql_text捞出来定位到具体代码。别继续加大参数那只是拖延。连接池 remove-abandoned 不生效。Druid 的remove-abandoned需要配合remove-abandoned-timeout使用单位是秒。如果设了但没日志检查log-abandoned是否为 true以及日志级别是否放开了 Druid 的 WARN。另外这个机制是「回收」不是「关闭游标」它把连接拿回池子但连接上挂着的游标可能还在所以根治还是要改代码。6. 语义一致收尾把排查链路固化成日常习惯游标问题本质上是资源管理问题。open_cursors调大是止血找到泄漏点并修复才是治本。日常可以养成几个习惯连接池开启泄漏检测定期查v$sesstat看有没有异常增长的会话代码 review 时重点看Statement、ResultSet、SqlSession的关闭路径。TaoToken 在这条链路里的价值是辅助分析不是替代数据库工具。把慢 SQL 丢给模型做模式归类把报错丢给模型生成排查脚本能省不少时间。需要 Key 的去 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 创建接入细节看 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 长期做编码和排查的可以考虑 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。最后留一个实用技巧把第 3 节那几条v$查询存成一个.sql文件出问题时直接cursor_check.sql跑一遍比临时敲命令快得多。游标数、会话占用、具体 SQL 三个维度一次看全定位效率会高很多。