news 2026/9/29 10:06:56

Errorstack 诊断 ORA-01000:maximum open cursors exceeded 的 open_cursors 配置与游标泄漏排查

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Errorstack 诊断 ORA-01000:maximum open cursors exceeded 的 open_cursors 配置与游标泄漏排查

1. 从一次 ORA-01000 报错说起:游标到底被谁占满了

应用日志里突然刷出ORA-01000: maximum open cursors exceeded,通常意味着某个会话打开的游标数量超过了open_cursors参数允许的上限。这个报错本身不复杂,麻烦的是它只告诉你“超了”,不告诉你“谁没关”。如果只是把open_cursors从 300 调到 3000,可能撑几天又爆,因为根因往往是代码里Statement/ResultSet没关,或者循环里反复创建游标却不释放。

我处理这类问题的思路是:先用 Errorstack 在报错瞬间把会话的游标现场 dump 下来,看清是哪些 SQL、哪些游标状态卡在 BOUND,再结合v$open_cursor和open_cursors参数判断是配置偏小还是泄漏。下面按“复现报错 → 设置 Errorstack → 抓 trace → 分析游标 → 调整参数 → 验证修复”走一遍,你可以直接照着在测试库上做一次。

适合谁看:Oracle DBA、Java/中间件开发、需要定位连接池游标泄漏的运维同学。核心检索词就是 Errorstack、ORA-01000、open_cursors、游标泄漏。

2. 前置准备:TaoToken 与排查环境

排查过程中如果要用大模型帮你读 trace、解释游标状态,或者让 coding agent 辅助写诊断脚本,可以先把 TaoToken 的接入配好。它提供 OpenAI 兼容接口,模型对话、API Key、Coding Plan 都有独立入口,按需选即可。

  • 官网入口:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
  • API 基址:https://taotoken.net/api
  • 模型对话(验证模型是否可用):https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite
  • Coding Plan(长期编码/Agent 场景):https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite
  • 控制台:https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite
  • API Keys:https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite
  • 接入文档:https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite

Oracle 侧需要:一个可复现的测试库(11g/12c/19c 均可)、能执行alter system的权限、能访问diag/rdbms/<db>/<inst>/trace目录。Java 侧准备一个故意不关游标的 demo,用来制造泄漏。

3. 可复制配置:复现 ORA-01000 并挂上 Errorstack

3.1 先把 open_cursors 调小,制造报错

为了快速复现,把参数临时调小。生产上不要这么干,测试库随意。

-- 查看当前值 show parameter open_cursors; -- 临时调小到 15,方便复现 alter system set open_cursors=15 scope=both; -- 确认 show parameter open_cursors;

scope=both表示内存和 spfile 同时生效,重启后仍是 15。复现完记得改回去。

3.2 设置 Errorstack 事件

Errorstack 的作用是:当指定错误号出现时,自动 dump 错误栈、进程栈和会话游标信息。针对 ORA-01000,事件号就是 1000。

-- 实例级:出现 ORA-01000 时 dump level 3 alter system set events '1000 trace name errorstack level 3'; -- 排查结束后关闭 alter system set events '1000 trace name context off';

Errorstack 的级别含义:

Level内容
1错误堆栈 + 函数调用堆栈
2Level 1 + ProcessState
3Level 2 + Context area(显示所有 cursors,重点显示当前 cursor)

排查游标泄漏用 level 3,因为只有它会把会话打开的游标列表打出来。也可以只在某个会话上设置:

alter session set events '1000 trace name errorstack level 3';

3.3 用 Java demo 触发泄漏

下面这段代码在循环里反复createStatement和executeQuery,但只在 finally 里关最后一次的rset/stmt,前面的游标全部泄漏。循环 300 次,open_cursors=15,必然报 ORA-01000。

import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; public class TestCursor { public static void main(String args[]) throws Exception { Connection con = null; Statement stmt = null; ResultSet rset = null; try { Class.forName("oracle.jdbc.driver.OracleDriver"); String url = "jdbc:oracle:thin:@127.0.0.1:1521:ora11"; con = DriverManager.getConnection(url, "test", "test"); for (int i = 0; i <= 300; i++) { stmt = con.createStatement(); rset = stmt.executeQuery("select * from test"); while (rset.next()) { rset.getString(1); } // 注意:这里没有 close,游标持续累积 } } catch (Exception e) { e.printStackTrace(); } finally { try { if (rset != null) rset.close(); if (stmt != null) stmt.close(); if (con != null) con.close(); } catch (Exception e) { e.printStackTrace(); } } } }

运行后会看到类似堆栈:

java.sql.SQLException: ORA-00604: 递归 SQL 级别 1 出现错误 ORA-01000: 超出打开游标的最大数 ORA-00604: 递归 SQL 级别 1 出现错误 ORA-01000: 超出打开游标的最大数 ORA-01000: 超出打开游标的最大数 at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:445) ... at TestCursor.main(TestCursor.java:19)

4. 验证请求与成功结果:读 trace 定位泄漏游标

4.1 找到 trace 文件

报错后,alert 日志里会出现类似:

OS Pid: 8588 executed alter system set events '1000 trace name errorstack level 3' Errors in file f:\app\administrator\diag\rdbms\ora11\ora11\trace\ora11_ora_8764.trc: ORA-01000: 超出打开游标的最大数

打开ora11_ora_8764.trc,重点看Session Cursor Dump和Session Open Cursors两段。

4.2 解读游标 dump

----- Session Cursor Dump ----- Current cursor: 0, pgadep=0 Open cursors(pls, sys, hwm, max): 15(0, 3, 15, 15) NULL=0 SYNTAX=0 PARSE=0 BOUND=15 FETCH=0 ROW=0

Open cursors(...): 15(0, 3, 15, 15)说明当前会话打开了 15 个游标,其中 3 个是系统递归游标,高水位和上限都是 15。BOUND=15表示 15 个游标全部处于 BOUND 状态——已经绑定但没关闭,典型的泄漏特征。

继续往下看:

----- Session Open Cursors ----- Cursor#1(0x000000001BD91998) state=BOUND curiob=0x000000001BDAD5B0 Cursor#5(0x000000001BD91BD8) state=BOUND curiob=0x000000001CBD94E8 ----- Dump Cursor sql_id=c99yw1xkb4f1u xsc=0x000000001CBD94E8 cur=0x000000001BD91BD8 ----- ObjectName: Name=select * from test Cursor#6(0x000000001BD91C68) state=BOUND curiob=0x000000001CBD8958 ----- Dump Cursor sql_id=c99yw1xkb4f1u xsc=0x000000001CBD8958 cur=0x000000001BD91C68 ----- ObjectName: Name=select * from test Cursor#9(0x000000001BD91E18) state=BOUND curiob=0x000000001CBD66A8 ----- Dump Cursor sql_id=c99yw1xkb4f1u xsc=0x000000001CBD66A8 cur=0x000000001BD91E18 ----- ObjectName: Name=select * from test

多个游标指向同一个sql_id=c99yw1xkb4f1u,SQL 文本都是select * from test。这说明应用在循环里反复执行同一条 SQL,每次新建游标却不关闭。到这里根因就清楚了:不是open_cursors太小,而是代码泄漏。

4.3 用 v$open_cursor 交叉验证

在报错会话还活着的时候,可以查:

-- 按会话统计打开的游标数 select s.sid, s.serial#, s.username, count(*) as cursor_cnt from v$open_cursor o, v$session s where o.sid = s.sid group by s.sid, s.serial#, s.username order by cursor_cnt desc; -- 看具体是哪些 SQL select sid, sql_id, sql_text from v$open_cursor where sid = <问题会话SID> order by sql_id;

如果某个 SID 的游标数接近open_cursors,且 SQL 高度重复,基本可以确认泄漏点。

5. 本篇常见错排查

5.1 Errorstack 设了但没生成 trace

先确认事件是否真的生效:

select name, value from v$parameter where name = 'event'; -- 或 show parameter event;

如果没看到1000 trace name errorstack level 3,可能是alter system没执行成功,或者被其他 event 覆盖。另外 trace 目录权限不足也会导致写不进去,检查background_dump_dest和user_dump_dest。

5.2 报错是 ORA-01000 但 trace 里没有 Session Open Cursors

大概率是 level 设成了 1 或 2。只有 level 3 才包含 Context area。改成 level 3 重新触发。

5.3 调大 open_cursors 后不报错了,但连接池还是异常

open_cursors是会话级上限,调大只是延后爆发。如果v$open_cursor里某个会话游标数持续增长不回落,说明泄漏仍在。正确做法是修代码:Statement、PreparedStatement、ResultSet用完即关,推荐 try-with-resources。

try (Connection con = DriverManager.getConnection(url, user, pwd); PreparedStatement ps = con.prepareStatement("select * from test"); ResultSet rs = ps.executeQuery()) { while (rs.next()) { rs.getString(1); } }

5.4 参数改了没生效

open_cursors是动态参数,scope=both立即生效。但如果用scope=spfile,需要重启。另外 RAC 环境要在每个实例上确认,或者用sid='*'。

alter system set open_cursors=1000 scope=both sid='*';

5.5 排查完忘记关 Errorstack

事件会一直挂在实例上,每次 ORA-01000 都 dump,trace 目录可能被撑爆。排查结束务必执行:

alter system set events '1000 trace name context off';

6. 收尾与后续接入

整个链路走下来:调小open_cursors复现 → 挂 Errorstack level 3 → 跑泄漏 demo → 读 trace 看到多个 BOUND 游标指向同一 sql_id → 用v$open_cursor确认 → 修代码用 try-with-resources → 把open_cursors调回合理值。根因是游标泄漏,不是参数太小,这一点在 trace 里看得很清楚。

如果你想让 coding agent 帮你批量扫描项目里没关的Statement,或者用模型解释 trace 里的游标状态,可以走 Coding Plan 和模型对话入口;API Key 和接入文档在下面:

  • API Keys:https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite
  • 接入文档:https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite
  • 模型对话:https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite
  • Coding Plan:https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite

最后提醒一句:生产库上设 Errorstack 前先确认 trace 目录空间,level 3 的 dump 在游标多的时候文件会比较大,别把磁盘写满。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 10:06:34

基于SpringBoot+Vue的律师服务预约系统-附源码

联系博主 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 …

作者头像 李华
网站建设 2026/9/29 10:06:20

MCP+Skills+A2A+DeepAgents:多智能体集群实战四件套

做Agent开发的朋友&#xff0c;最近估计都有同感&#xff1a;单Agent玩到一定程度就进瓶颈期了。上下文窗口撑不住&#xff0c;工具一多提示词就乱&#xff0c;任务稍微复杂一点&#xff0c;模型就开始“精神分裂”。带着这些问题&#xff0c;我花了一个多月时间把这套“DeepAg…

作者头像 李华
网站建设 2026/9/29 10:05:25

Git从入门到实战:版本控制、分支管理与冲突解决全指南

简介&#xff1a;Git作为分布式版本控制系统的代表&#xff0c;是解决多人协作开发与代码历史管理的常用工具。这份以“最详细、最傻瓜”为卖点的PDF教程&#xff0c;面向零基础开发者&#xff0c;从Git起源讲起&#xff0c;逐步覆盖Windows安装配置、版本库初始化、git add/co…

作者头像 李华
网站建设 2026/9/29 10:01:25

HTML 配 TaoToken:前端三大件 settings.json 骨架与报错排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华