ora-01000 根本原因是 java 应用未显式关闭 preparedstatement/resultset 导致游标泄漏,而非数据库 open_cursors 参数过小;须用 try-with-resources 按 resultset→preparedstatement→connection 顺序自动关闭,并禁用 statementcachesize 辅助定位。

ORA-01000 是代码没关游标,不是数据库配小了
ORA-01000 报错时别急着改 open_cursors。它本质是单个 Oracle 会话里“已解析但未关闭”的游标句柄堆积所致,默认值(通常 300 或 50)只是表象阈值。真正的问题在 Java 应用侧:每次调用 conn.prepareStatement() 或 conn.createStatement() 都会占用一个游标,而这些对象若未显式 close(),哪怕连接被连接池归还,游标仍挂在会话上不释放。
验证方法很简单:用 DBA 账号执行 SELECT COUNT(*) FROM v$open_cursor WHERE sid = ?,再对比你的应用线程对应的会话 ID —— 如果数值持续上涨、卡在 open_cursors 附近,基本可断定是泄漏。
必须用 try-with-resources 按 ResultSet → PreparedStatement → Connection 顺序关闭
手动 close() 极易出错,尤其在异常分支或循环中。常见错误包括:
-
PreparedStatement在 for 循环内反复 new,却只在循环外close()一次 —— 实际只释放了最后一个 - 只包了
Connection,没包PreparedStatement和ResultSet,导致后两者根本没触发close() - 用了
finally块但没判空,rs.close()抛 NPE 导致后续ps.close()被跳过
正确写法是三层独立包裹:
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement("SELECT * FROM emp WHERE id = ?");
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
// 处理数据
}
} // 自动按 rs → ps → conn 逆序 close()
禁用 statementCacheSize 是快速定位高发区的关键动作
很多框架和驱动默认开启语句缓存,比如 WebLogic 默认 statementCacheSize=200,Oracle UCP 和某些 JDBC URL 也自带缓存。这时调用 ps.close() 实际只是把 PreparedStatement 放回池子,不是销毁游标 —— 它仍在会话中保持解析状态。
临时禁用能快速验证是否为缓存惹祸:
- JDBC Thin URL 加参数:
jdbc:oracle:thin:@host:1521/orcl;statementCacheSize=0 - HikariCP + Spring Boot:
spring.datasource.hikari.data-source-properties.statementCacheSize=0 - UCP:
connectionPool.setStatementCacheSize(0)
禁用后若 ORA-01000 消失,说明原缓存配置过大;日常够用的值通常是 20~50,不必保留 200。
调大 open_cursors 只是兜底手段,且有隐藏代价
DBA 执行 ALTER SYSTEM SET open_cursors = 1000 SCOPE=BOTH 能缓解症状,但它掩盖了真实泄漏点,且每个游标会消耗 PGA 内存。盲目设到 5000 不仅无益,还可能引发内存争用甚至实例抖动。
只有满足以下条件时才考虑调参:
- 确认单会话确实需并发执行大量不同 SQL(如动态报表拼接 50+ 子查询)
- 已用
v$open_cursor确认游标分布合理、无重复 SQL 占堆 - 代码层所有
PreparedStatement/ResultSet已严格按 try-with-resources 管理
最常被忽略的一点:即使调大了 open_cursors,如果代码里还在循环中反复 prepareStatement() 却不 close,游标照样涨满——只是报错时间延后而已。











