2009-02-04 95 views
0

如果我从应用程序中的多个线程调用以下代码,是否存在死锁风险?用于连接数据库的事务在此调用之前打开,一旦返回就关闭。 应用程序:java 数据库:Oracle使用Rownum时数据库死锁?

FUNCTION reserveWork(in_batch_id NUMBER, 
         in_work_size NUMBER, 
         in_contentType_id NUMBER) RETURN NUMBER IS 
    rows_reserved NUMBER := 0; 

    BEGIN 
    UPDATE 
      D_Q1 
    SET 
      DQ1_BAT_ID = in_batch_id 
    WHERE 
     DQ1_BAT_ID is null 
     AND DCT_ID = in_contentType_id 
     AND ROWNUM < (in_work_size + 1); 

    rows_reserved := SQL%ROWCOUNT; 

    RETURN (rows_reserved); 

    END; 

回答

2

为了发生死锁,你必须有这两个条件。

  1. 每个事务都必须有多个锁。

  2. 锁必须按不同的顺序抓住。

条件1为true,因为每个线程锁定多行。 条件2在理论上是正确的,因为返回行的顺序不是确定性的。例如,线程1可能会尝试更新行1,2,3,线程2可能会尝试更新行3,2,1。

实际上,Oracle可能总是以相同的顺序返回行,所以它可能永远不会死锁。无论如何,准备好处理ORA-00060错误并重新提交请求。

另一个想法是在两个步骤中做到这一点。第一个过程执行SELECT * WHERE ... FOR UPDATE NO WAIT锁定行,如果没有返回ORA-00054,则第二个过程执行实际更新。否则,您重试。

无论采用哪种方式,请确保您的CREATE TABLE中的INITTRANS设置为尽可能多的客户端,并将同时更新该表。

+0

哇,我读过汤姆凯特的答案在下面的加里的答案中引用。很酷!这意味着如果事务阻止它将被重新启动,所以它不应该死锁。 – 2009-02-04 23:44:24

1

还有,如果你正在运行在同一个表多次更新一个明确的僵局风险。

特别是因为我在代码中看不到COMMIT或ROLLBACK?我认为这是在JDBC中完成的?

更新时间越长,死锁风险就越高。

+0

是,提交/回滚以下根据是否产生或也不例外此函数的调用立即进行。 – Adam 2009-02-04 18:49:12

1

死锁而事务B上已经被交易A.锁定的等待记录发生在事务A锁定了一个记录,则必须等待事务B开锁记录,

Oracle有一个相当复杂的机制在更新过程中处理对表格的更改。见

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:11504247549852 

一般来说,死锁的风险增加了长事务中运行和更多的数据交易的变化。我认为这不太可能造成死锁,但很可能会“排队” - 如果您有三个或四个并发会话运行此SQL,则每个会话将具有相同的SQL执行路径,将识别相同的行以进行更新,一个会先到达他们,另一个会等待。当第一个事务完成时,另一个将重新抓取记录,发现它们被更改,然后重新启动,如Tom Kyte的文章中所述,并选择下一堆行。

如果您使用的是11g,那么您可以使用SKIP LOCKED。它在早期版本中存在,但没有记录。所以在那里它会被使用在你自己的风险。

http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/statements_10002.htm#SQLRF01702 

这样一来,你

SELECT primary_key BULK COLLECT INTO pk_variable_array FROM D_Q1 
WHERE DQ1_BAT_ID is null 
AND DCT_ID = in_contentType_id 
AND ROWNUM < (in_work_size + 1) 
FOR UPDATE SKIP LOCKED; 
-- 
FORALL i in 1..pk_variable_array 
UPDATE D_Q1 
SET DQ1_BAT_ID = in_batch_id 
WHERE primary_key = pk_variable_array(i)