YashanDB 报错 YAS-04003 排查:OPEN_CURSORS 参数与游标泄漏定位的配置骨架
1. 生产库突然报 YAS-04003先别急着改参数YashanDB 报错YAS-04003: maximum number of open cursors is 310直译过来就是「当前会话打开的游标数超过了数据库允许的上限」。游标你可以理解成数据库给每条 SQL 执行结果开的一个「取数窗口」应用每执行一次查询、每打开一个 ResultSet背后往往就对应一个游标。窗口开得太多又不关数据库就会直接拒绝新的请求业务侧表现通常是接口大面积报错、连接池被打满。这个报错适合谁看任何在用 YashanDB 跑生产业务、尤其是 JDBC/连接池/ORM 框架用得比较重的团队。它最坑的地方在于它不一定是数据库配置太小很多时候是应用侧游标泄漏。如果你上来就把OPEN_CURSORS从 310 调到 5000可能只是把爆炸时间往后推了几个小时根因还在。我这篇按真实排查顺序走一遍先确认当前会话和全局的游标占用再看OPEN_CURSORS参数怎么查怎么改然后给一套能定位到具体 SQL 的排查语句最后讲调整后怎么重连验证、怎么防复发。全程给可复制的 SQL你对着自己的库就能跑。2. TaoToken 前置把排查助手接进来排查这种问题光靠数据库客户端不够你还需要一个能随时问 SQL 写法、帮你解释执行计划、生成排查脚本的助手。我平时用的是 TaoToken它把主流大模型的对话、编码能力聚合到一个入口注册后拿 API Key 就能在客户端或脚本里调用不用自己一个个平台去配。它的定位很简单一个 API 入口调用多家模型。对 DBA 和运维来说最实用的场景就是——遇到不熟的报错直接把错误码和上下文丢给模型让它给出排查方向再自己到库里验证。官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 这个不加 UTM。具体怎么接先到控制台创建 API Keyhttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite想直接对话问排查思路用模型对话页https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite如果你要长期写排查脚本、做自动化巡检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注意TaoToken 是模型调用入口不是数据库客户端也不替代 YashanDB 本身。排查动作仍然要在你的数据库会话里执行。3. 可复制配置查参数、改参数、查游标占用3.1 先确认 OPEN_CURSORS 当前值YashanDB 默认OPEN_CURSORS是 310报错信息里的数字通常就是它。先查清楚当前生效值别凭记忆-- 查看当前 OPEN_CURSORS 生效值 SHOW PARAMETER OPEN_CURSORS; -- 或者从参数视图查 SELECT name, value, default_value, is_default FROM v$parameter WHERE name OPEN_CURSORS;如果is_default是TRUE说明你从没改过用的就是默认 310。这一步很关键因为有些环境被人改过但没记录直接按 310 去推会算错。3.2 查当前游标占用情况光知道上限没用得知道现在开了多少、是谁开的。YashanDB 提供会话级和系统级的游标统计视图下面这几条按需取用-- 按会话统计当前打开的游标数倒序排列 SELECT sid, serial#, username, status, osuser, machine, program, opened_cursors FROM v$session WHERE opened_cursors 0 ORDER BY opened_cursors DESC; -- 系统级汇总当前总打开游标数 SELECT SUM(opened_cursors) AS total_opened_cursors FROM v$session; -- 查看某个具体会话打开了哪些游标需要对应权限 SELECT sid, sql_id, cursor_type, child_address FROM v$open_cursor WHERE sid target_sid ORDER BY sql_id;v$session.opened_cursors这个字段是排查核心它告诉你每个会话手里攥着多少个没关的游标。如果某个应用连接池的会话opened_cursors长期几百不降基本可以锁定是它泄漏。3.3 调整 OPEN_CURSORS 参数确认业务确实需要更多游标、且短期无法改代码时可以调参数。YashanDB 支持会话级和系统级两种改法-- 系统级调整立即生效重启后仍保留视版本持久化策略 ALTER SYSTEM SET OPEN_CURSORS 500; -- 只对当前会话生效适合临时救急验证 ALTER SESSION SET OPEN_CURSORS 500;改完再查一次确认SHOW PARAMETER OPEN_CURSORS;注意ALTER SYSTEM改的是全局默认新会话按新值走但已经存在的旧会话不会自动变大。这点后面验证章节会重点讲很多人改完发现还报错就是没重连。3.4 参数取值参考场景建议 OPEN_CURSORS说明默认/轻量业务310默认值够用就别动中等并发 OLTP500–1000连接池会话多、SQL 种类多复杂批处理/报表1000–2000单会话可能开大量游标已确认泄漏先别调优先修代码调参数只是止血数值不是越大越好。每个游标都占内存盲目调到几万会拖垮实例。原则是先定位泄漏再按真实峰值留 30% 余量。4. 验证请求重连后确认游标回落改完参数别急着宣布恢复按下面步骤验证否则你看到的可能是假象。第一步让应用侧断开重连或者重启连接池确保新会话用上新参数-- 查当前会话的 OPEN_CURSORS 生效值 SELECT sid, username, opened_cursors FROM v$session WHERE sid SYS_CONTEXT(USERENV, SID);第二步跑一轮业务请求再观察游标数是否随请求结束而回落-- 连续观察两次间隔几十秒看 opened_cursors 是否下降 SELECT sid, username, opened_cursors FROM v$session WHERE username YOUR_APP_USER ORDER BY opened_cursors DESC;正常情况应该是请求高峰时游标数上升请求结束后回落到一个稳定基线。如果它只涨不跌说明泄漏还在调参数只是把天花板抬高了。第三步用一条真实业务 SQL 验证不再触发报错-- 模拟应用查询确认能正常返回 SELECT COUNT(*) FROM your_business_table WHERE create_time SYSDATE - 1;如果之前报 YAS-04003 的接口现在能正常返回且opened_cursors稳定才算真正恢复。5. 本篇常见错排查5.1 改完参数还报 YAS-04003最常见原因就是旧会话没重连。ALTER SYSTEM不影响已存在的会话连接池里的老连接还按 310 算。解决办法是重启应用或让连接池做一次全量重建。验证方法就是查那个报错会话的opened_cursors和它实际能开的上限。5.2 找不到 v$open_cursor 或权限不足普通业务账号通常没有查动态视图的权限。用 DBA 账号或者让管理员授权GRANT SELECT ON v$session TO your_app_user; GRANT SELECT ON v$open_cursor TO your_app_user;如果视图名对不上先查v$fixed_table确认当前版本有哪些游标相关视图SELECT name FROM v$fixed_table WHERE name LIKE %CURSOR%;5.3 游标数一直不降但找不到具体 SQL这种情况多半是应用没关 ResultSet 或 Statement。排查思路先按program、machine分组定位到是哪个应用节点SELECT program, machine, COUNT(*) AS session_cnt, SUM(opened_cursors) AS total_cursors FROM v$session WHERE opened_cursors 0 GROUP BY program, machine ORDER BY total_cursors DESC;锁定节点后再去那个应用里查代码有没有在finally块里关ResultSet/Statement/ConnectionORM 有没有配置游标复用连接池有没有开 statement 缓存但没设上限。5.4 调大后内存上涨明显游标是占内存的调太大确实会推高 PGA。如果发现调整后实例内存吃紧先降回来改用「修代码 会话级临时调大」的组合而不是全局拉高。6. 后续怎么接把排查能力固化下来排查完这一次建议把上面几条 SQL 存成一个巡检脚本定时跑监控SUM(opened_cursors)的基线。一旦某天它持续高于历史均值就提前介入别等报错。如果你想把「报错解释 SQL 生成 脚本编写」这套流程自动化可以用 TaoToken 的 API 把模型接进你的运维脚本API 地址 https://taotoken.net/api Key 在 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 创建。长期写这类巡检和排障脚本的话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 。最后留一个我踩过的坑有次调完OPEN_CURSORS以为万事大吉结果第二天又报查下来是连接池的 statement 缓存把游标一直攥着不放。所以参数只是止血真正要盯的是应用侧有没有把close()写进finally。