2009-02-04 99 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)