
db2报错`sqlcode=-904, reason 00c90096`本质是事务累积锁(页锁/行锁)超限,源于多线程未及时提交导致锁资源耗尽;解决核心是控制单事务锁数量、合理分批提交或调优数据库参数。
该错误(com.ibm.db2.jcc.am.SqlException: UNSUCCESSFUL EXECUTION CAUSED BY AN UNAVAILABLE RESOURCE. REASON 00C90096)是DB2典型的资源限制类异常,对应SQLSTATE 57011 和 SQLCODE -904。其根本原因并非并发本身违法,而是单个数据库代理(agent)在事务中持有的页锁(page lock)或行锁(row lock)数量达到了系统配置上限 NUMLKUS(Number of Locks per User),触发了保护性拒绝。
错误信息中的 TYPE OF RESOURCE 00000304 表示锁资源类型(DB2内部编码,通常对应数据页锁),而 RESOURCE NAME X'0005AB8C'.X'02' 指向被争用的具体数据页位置——这印证了问题出在锁粒度与数量失控,而非连接池或表结构问题。
✅ 正确实践:分批提交 + 显式事务控制
在多线程批量插入场景中,绝对避免单个事务插入成千上万条记录。应将大批次拆分为小批次(如每100–500条为一个事务),并显式提交:
// 示例:JDBC 多线程安全的分批插入(伪代码) public void batchInsert(Listrecords, int batchSize) { try (Connection conn = dataSource.getConnection()) { conn.setAutoCommit(false); // 关闭自动提交 String sql = "INSERT INTO my_table (col1, col2) VALUES (?, ?)"; try (PreparedStatement ps = conn.prepareStatement(sql)) { for (int i = 0; i < records.size(); i++) { Record r = records.get(i); ps.setString(1, r.getCol1()); ps.setString(2, r.getCol2()); ps.addBatch(); // 每 batchSize 条执行一次提交 if ((i + 1) % batchSize == 0 || i == records.size() - 1) { ps.executeBatch(); conn.commit(); // 显式提交,释放锁资源 ps.clearBatch(); } } } } catch (SQLException e) { // 记录完整堆栈,回滚当前批次 throw new RuntimeException("Batch insert failed at batch " + ((i / batchSize) + 1), e); } }
⚠️ 注意事项: 不要跨线程共享 Connection 或 PreparedStatement —— 每个线程应独占连接(通过连接池获取); 避免在循环内频繁 commit()(如每条都提交),会严重降低性能;推荐 batchSize = 100~500,需结合表索引数、行宽和锁升级阈值实测调整; 若使用 Spring @Transactional,务必确保传播行为为 REQUIRES_NEW 或明确控制事务边界,避免事务范围意外扩大。
? 可选优化:数据库级调优(需DBA协同)
若业务逻辑强制要求长事务,可由DBA评估以下参数(不建议开发直接修改):
- 增加 NUMLKUS(需重启实例,影响全局内存);
- 启用 LOCKSIZE ROW(建表时指定)减少锁粒度;
- 调整 MAXLOCKS(锁占用百分比阈值)防止过早锁升级;
- 开启 CURSOR WITH HOLD 配合 FOR UPDATE 时注意锁持续时间。
✅ 总结
REASON 00C90096 是DB2对“锁资源过载”的明确告警,本质是事务设计不合理。最佳解法永远是:以可控批次替代巨型事务,用显式 commit() 主动释放锁,辅以连接隔离与监控。与其调参治标,不如重构事务边界——这是高并发写入场景下稳定、可扩展的黄金准则。










