ora-01000 是单个会话游标未关闭导致超出 open_cursors 限制,该参数是会话级软限制,非实例级总和;需排查 jdbc 或 pl/sql 中游标泄露,而非盲目调大参数。
ora-01000 错误说明与 open_cursors 的真实作用
出现 ora-01000: maximum open cursors exceeded,不是数据库“太忙”,而是当前会话打开的游标没及时关闭,超出了该会话允许的上限。注意:open_cursors 是**每个会话**(session)的软限制,不是实例级总和;它控制的是单个连接能同时保持打开状态的游标数,包括显式声明的 pl/sql 游标、隐式 sql 执行产生的游标(如 dml、select),甚至 jdbc 中 preparedstatement 对应的服务器端游标。
常见误解是调大就万事大吉——但治标不治本,掩盖了真正的资源泄露。
- 默认值通常为 300,对多数 OLTP 应用偏小,但设到 2000+ 仍频繁报错,基本可判定存在游标未关闭
- 该参数修改需重启实例(
ALTER SYSTEM SET open_cursors=... SCOPE=SPFILE),无法热生效 -
V$OPEN_CURSOR视图只显示当前会话打开的游标,查不到其他会话;排查泄露必须结合V$SESSION和V$SQL
Java 应用中 PreparedStatement 泄露的典型场景
JDBC 层是最常见的 open_cursors 泄露源头,尤其在未正确关闭语句对象时。Oracle JDBC 驱动默认将 PreparedStatement 缓存为服务端游标,即使 Java 端 close() 调用失败或被跳过,游标也不会自动释放。
- 忘记在
finally或 try-with-resources 中关闭PreparedStatement或ResultSet - 使用连接池(如 HikariCP)时,归还连接前未清理语句(部分老版本驱动或配置不当会导致语句残留)
- 在循环内反复创建
PreparedStatement却复用同一变量名,旧对象因引用丢失而无法 close - Spring
JdbcTemplate默认不缓存语句,但若手动传入PreparedStatementCreator且未管理生命周期,同样会泄露
验证方式:登录数据库执行 SELECT sid, sql_id, sql_text FROM v$open_cursor WHERE sid = :your_sid ORDER BY last_sql_time DESC,看是否大量重复 SQL 或长时间未更新的游标。
PL/SQL 中显式游标未关闭的检查要点
PL/SQL 块里用 DECLARE ... CURSOR c IS ...; OPEN c; ... CLOSE c; 模式时,如果异常路径未覆盖 CLOSE,就会导致游标滞留。更隐蔽的是使用 FOR c IN (SELECT ...) LOOP 形式——它自动 OPEN/CLOSE,但底层仍消耗一个游标槽位,且不可复用。
- 所有
OPEN必须配对CLOSE,且放在EXCEPTION块中(或用BEGIN ... EXCEPTION WHEN OTHERS THEN IF c%ISOPEN THEN CLOSE c; END IF;) - 避免在包级变量中长期持有游标(如
PACKAGE CURSOR c IS SELECT ...),这类游标生命周期与会话绑定,极易超限 - 批量处理时优先用 BULK COLLECT + LIMIT,减少逐行 OPEN/CLOSE 次数;但注意
BULK COLLECT本身也会隐式打开游标
示例错误写法:OPEN emp_cur; FETCH emp_cur INTO rec; -- 若这里异常,emp_cur 永远不关
快速定位泄露会话与临时缓解措施
生产环境不能等重启,先锁定问题源头再调整参数。关键不是“谁开了最多游标”,而是“谁开了不该开的游标”。
- 查高游标占用会话:
SELECT s.sid, s.serial#, s.username, s.machine, s.program, s.status, COUNT(*) cnt FROM v$open_cursor o, v$session s WHERE o.sid = s.sid GROUP BY s.sid, s.serial#, s.username, s.machine, s.program, s.status ORDER BY cnt DESC FETCH FIRST 5 ROWS ONLY - 对嫌疑会话启用 SQL Trace:
EXEC DBMS_MONITOR.SESSION_TRACE_ENABLE(session_id => xxx, serial_num => yyy, waits => TRUE),分析 trace 文件中 OPEN/CLOSE 是否匹配 - 临时缓解可用
ALTER SYSTEM KILL SESSION 'sid,serial#'终止泄露会话(注意事务回滚开销) - 确认是应用层问题后,宁可加监控告警(如每 5 分钟查
V$SESSION_OPEN_CURSOR并发量),也不要盲目调高open_cursors
真正难的不是改参数,是让开发意识到:Oracle 的游标不是“用完即焚”的轻量对象,而是一个需要显式归还的服务器资源。漏关一次,可能撑满整个会话额度。










