大型Oracle INSERT语句执行停滞,触发ORA-00054资源占用错误
解决ORA-00054: resource busy错误的思路(针对批量XML导入场景)
嘿,这个ORA-00054的坑我之前踩过好几次!结合你用C# + Dapper批量导入13个XML到Oracle的场景,咱们来捋捋可能的原因和解决办法:
先搞懂错误本质
ORA-00054说白了就是:你要操作的数据库资源(表、行或者锁)被别的会话占着,而你的操作因为设置了NOWAIT(或者等待超时),没法获取到锁就直接报错了。结合你跑了31分钟才出问题,大概率是长时间运行的操作积累了锁,或者中途出现了资源冲突。
排查&解决步骤
1. 先定位谁在占着资源
先登录Oracle数据库,跑这两个SQL查当前的锁情况:
-- 查被锁定的对象 SELECT lo.object_id, do.object_name, lo.session_id, lo.locked_mode FROM v$locked_object lo JOIN dba_objects do ON lo.object_id = do.object_id; -- 查持有锁的会话详情 SELECT s.sid, s.serial#, s.username, s.program, s.status FROM v$session s WHERE s.sid IN (SELECT session_id FROM v$locked_object);
- 如果发现是你自己的导入会话占着锁,那大概率是代码里的事务没及时提交;
- 如果是别的会话(比如其他开发人员的查询、更新,或者其他定时任务),那得协调暂停这些操作,或者调整你的导入时间。
2. 检查事务和连接管理
长时间运行的批量操作最容易犯的错就是长事务:比如把所有13个XML的插入都放在一个大事务里,31分钟的事务会一直持有锁,不仅容易引发冲突,万一中间出错回滚也会巨慢。
- 解决办法:拆分事务,比如每插入N条数据就提交一次(比如每1000条Commit一次)。示例代码:
using (var connection = new OracleConnection(connectionString)) { connection.Open(); var transaction = connection.BeginTransaction(); try { int batchSize = 1000; for (int i = 0; i < dataList.Count; i += batchSize) { var batch = dataList.Skip(i).Take(batchSize).ToList(); string sql = "INSERT INTO your_table (col1, col2) VALUES (:Col1, :Col2)"; connection.Execute(sql, batch, transaction: transaction); // 每批提交一次 transaction.Commit(); transaction = connection.BeginTransaction(); } // 提交最后一批 transaction.Commit(); } catch (Exception ex) { transaction.Rollback(); throw; } }
另外,一定要确保用using块管理数据库连接,避免连接泄漏导致会话堆积锁资源。
3. 优化批量插入的效率
慢操作更容易碰到锁冲突,因为占用资源的时间太长。试试这些优化:
- 用Dapper的批量
Execute方法(上面的示例就是),比循环单条插入快得多; - 直接用
OracleBulkCopy(Oracle官方驱动里的类),把数据转成DataTable后批量导入,效率是普通INSERT的好几倍,能大幅缩短操作时间,减少锁冲突概率。示例:
DataTable dataTable = ConvertToDataTable(yourEntityList); // 自己实现实体转DataTable的方法 using (var connection = new OracleConnection(connectionString)) { connection.Open(); using (var bulkCopy = new OracleBulkCopy(connection)) { bulkCopy.DestinationTableName = "YOUR_TABLE"; // 映射列名(如果实体和表列名一致可以省略) bulkCopy.ColumnMappings.Add("Col1", "COL1"); bulkCopy.ColumnMappings.Add("Col2", "COL2"); bulkCopy.WriteToServer(dataTable); } }
4. 排查隐含的DDL操作
如果你的表上有触发器,或者插入过程中调用了存储过程,要检查这些逻辑里有没有执行DDL(比如创建索引、修改表结构、更新统计信息)。DDL操作需要排他锁,一旦执行就会锁表,后续的插入都会失败。
- 解决办法:临时禁用触发器(导入完成后再开启),或者把DDL操作移到导入完成后执行。
5. 调整超时设置
如果错误提示里有“timeout expired”,可以适当调大Dapper的commandTimeout参数,给操作更多等待锁的时间,但这只是缓解办法,核心还是要解决锁的来源:
connection.Execute(sql, dataList, commandTimeout: 600); // 设为10分钟
最后总结
先查锁定位问题根源,再从事务拆分、连接管理、插入效率这几个核心点优化,基本就能解决这个问题。如果是外部会话占锁,那就得协调资源使用时间啦!
内容的提问来源于stack exchange,提问作者Jordec
相关产品推荐
相关产品推荐

