Oracle觸發器死鎖問題解決

regonly1發表於2009-06-26

這兩天一直在因為系統初期設計原因導致的一個觸發器問題。
問題如下:
有表T,有客戶編號、賬戶編號及地址三個欄位(為方便起見以最少欄位描述)。
一個客戶編號下可能存在多個賬戶編號(3個或4個)。
假設客戶編號C0下有A1、A2、A3三個賬戶編號。
        現在對賬戶編號A1的地址欄位進行了更新,要求透過觸發器同時更新客戶編號C0下的另外兩個賬戶A2和A3的地址欄位。

透過實際的觸發,發現存在著一個非常致命的問題:
       由於指定的是自治事務觸發器(即指定了pragma autonomous_transaction),假設為T1。每次更新表時觸發都是獨立的。因此就產生了死迴圈和死鎖的問題。
因為更新了A1,則T1觸發。T1內去找到同個客戶編號下的A2和A3。然後先更新A2,此時便又觸發了T1。然後又找到同個客戶編號下的A1和A3。然後先更新A1,而由於先前第一次更新已經鎖住了A1,再次更新就會導致死鎖。因為先前的A1更新需要現在的A1先完成。而現在的A1更新卻被先前的A1更新阻塞了。
        從上面可以看出,如果可以找到這樣一個辦法,將鎖住的行找出來,然後在每次更新之前都判斷一下該行是否被鎖住。如果鎖住則跳到下一條記錄進行處理。沒鎖住則繼續更新。
       實際上這只是理想的情況,的確可以透過查詢v$lock v$session v$locked_object查到被鎖住的行:
select ta.account_no, o.object_name, ta.rowid
 from v$session s, ttest ta, user_objects o, v$locked_object lo
where lo.SESSION_ID = s.SID
  and lo.OBJECT_ID = o.object_id
  and dbms_rowid.rowid_object(ta.rowid) = o.data_object_id
  and o.object_id = s.ROW_WAIT_OBJ#
  and dbms_rowid.rowid_relative_fno(ta.rowid) =  s.ROW_WAIT_FILE#
  and dbms_rowid.rowid_block_number(ta.rowid) = s.ROW_WAIT_BLOCK#
  and dbms_rowid.rowid_row_number(ta.rowid) = s.ROW_WAIT_ROW#
但是透過實際除錯發現。這個記錄的並不是當時被鎖住的所有記錄。而是最後一次被鎖住的一條記錄。什麼意思呢?也就是說當時可能有幾條記錄被鎖住。但是這個SQL所查出來的是執行所有鎖行操作的語句後,最後一次被鎖住的行。也就是失去了即時判斷的可能性。
       想透過另外的方法去找到這個被鎖住的行我網上也搜尋過無數遍了。都沒有實際解決方案。可能這麼做的人很少吧。
       最後想到了一個辦法,就是透過臨時表的方法去記錄被更新的行。一旦觸發T1,就往臨時表記錄這一行的rowid和對應的鎖定標識。然後在更新的時候判斷
是否已經存在於該表中。如果有則不更新,沒有則往下更新。更新並提交後該行
鎖標識清零。進入下一次迴圈。
       這個臨時表是會話級的。因此在觸發器迴圈觸發過程中都是處於同一個資料環境下。方便了對指定行的加鎖與解鎖(其實是設定行鎖定狀態)。
下一篇準備講述此次解決死鎖問題而學習到的關於Oracle鎖的一些方面的知識。
(備註:如果不指定自治事務觸發器,則無法修改觸發器觸發所在的表)

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12932950/viewspace-607691/,如需轉載,請註明出處,否則將追究法律責任。

相關文章