之前我使用过这个查询,我一个接一个地更新100行。
String query="update table set status=? where rownum< ?";
stmt = conn.prepareStatement(query);
for(int i=0; i < CHUNK_SIZE;i++){
stmt.setString(1, "STATUS_IN_PROGRESS");
stmt.setInt(2, 2);
stmt.executeUpdate();
}
现在我更改了此查询以立即更新所有行。
String query="update table set status=? where rownum< ?";
stmt = conn.prepareStatement(query);
stmt.setString(1, "STATUS_IN_PROGRESS");
stmt.setInt(2, CHUNK_SIZE);
但后者正在给予&#34; ORA-00060:在等待资源时检测到死锁&#34;例外。我读到了this,但是我无法理解,如果异常是因为竞争DML,那么它应该在第一个查询中发生,但概率较低,但情况并非如此。
此外,我们通常会在数据库级别进行频繁更新,因此它应该是一个常见的问题,但事实并非如此。
答案 0 :(得分:4)
正如Alex Poole建议的那样,你肯定想要寻找跟踪文件。每个死锁都会在数据库上创建一个单独的跟踪文件。该文件列出了所有相关对象和SQL语句。不要假设你知道哪些语句导致了死锁,有几种奇怪的方法可能会发生死锁。
正如ibre5041所指出的,死锁取决于检索数据的 order 。但是,只需添加ORDER BY
即可获得帮助。具有相同执行计划的相同语句将始终以相同的顺序返回数据(实际上,但这不保证!)。但是在某些情况下,相同的SQL语句可以有不同的执行计划。例如,如果CHUNK_SIZE绑定变量不同,则可能导致执行计划发生更改。找到SQL语句并检查多个执行计划可能会有所帮助,并尝试修复一个计划。此查询可以帮助您查找语句和计划:
select sql_id, plan_hash_value from gv$sql where lower(sql_text) like '%table_name%';
自动提交可以解释为什么第一个版本不会导致错误。死锁需要两次交易,其中至少有一次必须已完成工作,然后尝试做更多的工作。交易必须持有锁并要求另一个交易。如果事务在每一行之后提交,则无法发生死锁。但是,我并不主张单行处理。
解决方案通常是模式修复(在外键上添加索引,将位图索引转换为b树索引),控制对表的访问(序列化访问或至少确保语句处理在相同的顺序),作为最后的手段使用异常处理。