oracle锁bug

阅读更多
一、问题描述
两任务并发访问同一数据库表中的记录,出现死锁,如下是其中一个任务的代码:
  1. Connection dbConn = DAMContext.getConnection();  
  2. String userAccountSql = createAccountSql(exportDate);  
  3. List accountList = new ArrayList();  
  4. try {  
  5.     PreparedStatement pstm = dbConn.prepareStatement(userAccountSql,  
  6.         ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);  
  7.     ResultSet result = pstm.executeQuery();  
  8.     while (result.next()) {  
  9.         UserAccount account = new UserAccount();  
  10.         //omited  
  11.         accountList.add(account);  
  12.     }  
  13.   
  14.     String updateSql = createExpireSql(exportDate);  
  15.     PreparedStatement updatePs = dbConn.prepareStatement(updateSql,  
  16.            ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); 
  17.     //ResultSet.CONCUR_READ_ONLY 表示sql语句不会做更新处理,  
  18.     //这就意味者在操作这批记录时,不会首先获取这批记录的"页锁",别人仍旧可以同时对这批数据进行并发的update操作。  
  19.   
  20.     ResultSet updateResult = updatePs.executeQuery();  
  21.     //执行update操作  
  22.     //在dbConn.prepareStatement()中指定的是不做update处理,而实际上却执行了update操作  
  23.     //导致更新这批记录时:更新到某条记录时,才获取这条记录的"独占行锁"。  
  24.     //这就留下了发生死锁的隐患:在执行过程中,一部分已更新的记录集(A)获取了锁;另一部分还没有更新的记录集(B)没有获取锁  
  25.     //如果此时,有另一线程已经update了记录集(B)中的记录,并且未释放锁;同时又等待记录集(A)中记录的锁来进行update操作,这样死锁就发生了。  
  26. }  
  27. catch (Exception e) {  
  28.     e.printStackTrace();  
  29.     try {  
  30.         dbConn.rollback();  
  31.     }  
  32.     catch (SQLException _ex) {  
  33.         _ex.printStackTrace();  
  34.     }  
  35. }  
  36. finally {  
  37.     try {  
  38.         dbConn.setAutoCommit(true);  
  39.     }  
  40.     catch (SQLException _ex) {  
  41.         _ex.printStackTrace();  
  42.     }  
  43.     finally {  
  44.         DAMContext.freeConnection(dbConn);  
  45.     }  
  46. }  

二、解决方案一
确保两个线程访问同一个数据库表的记录的顺序都是一致的。
因为:假设线程(一)目前处理的状况是已更新的为数据集A,剩下未更新的为数据集B;由于两线程访问数据库表的顺序是一致的,故必须是先访问数据集A中的记录,然后再访问数据集B中的记录,因此线程(二)肯定是先访问数据集A中的记录,而此时必须等待,直至线程(一)commit或rollback释放全部的“行锁”。

三、解决方案二
  1. PreparedStatement updatePs = dbConn.prepareStatement(updateSql,  
  2.        ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);  
修改为:
  1. PreparedStatement updatePs = dbConn.prepareStatement(updateSql,  
  2.        ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);  

ResultSet.CONCUR_UPDATABLE 表示sql语句是要做更新操作,那么在更新这批记录集前,就会先拿到"页锁";如果这批记录集的部分记录的“行锁”未释放,则等待这批记录的“行锁”全部释放,获取“页锁”。在“页锁”释放掉之前,别人不可能拿到这批记录集中的任何一条记录的“行锁”,也就不可能发生死锁。


你可能感兴趣的:(Oracle,SQL)