存儲過程編譯出現鎖死情況的解決方法 存儲過程編譯出現鎖死情況的解決方法

存儲過程編譯出現鎖死情況的解決方法

本隨筆文章,由個人博客(鳥不拉屎)轉移至博客園
發佈時間: 2018 年 10 月 23 日
原地址:https://niaobulashi.com/archives/procedures_locks.html


存儲過程

先科普一下存儲過程,在項目開發過程可能會遇到。

存儲過程(Stored Procedure)是在大型數據庫系統中,一組爲了完成特定功能的SQL 語句集,存儲在數據庫中,經過第一次編譯後再次調用不需要再次編譯,用戶通過指定存儲過程的名字並給出參數(如果該存儲過程帶有參數)來執行它。 存儲過程是數據庫中的一個重要對象。


今天開發過程中,遇到之前寫好的存儲過程重新進行編譯出現鎖死的情況,於是我瞬間不淡定了。明明之前都已經寫好了呀!

在數據庫開發的過程中,經常碰到包、存儲過程、函數無法編譯或編譯時導致PL/SQL無法響應的問題。碰到這種問題,基本上都要重啓數據庫解決,嚴重浪費開發時間。

問題分析

從事數據庫開發的都知道鎖的概念,如:執行 Update Table xxx Where xxx 的時候就會產生鎖。這種常見的鎖在Oracle裏面被稱爲DML鎖。在Oracle中還有一種DDL鎖,主要用來保證存儲過程、表結構、視圖、包等數據庫對象的完整性,這種鎖的信息可以在DBA_DDL_LOCKS中查到。注意:V$LOCKED_OBJECT記錄的是DML鎖信息,DDL鎖的信息不在裏面。

對應DDL鎖的是DDL語句,DDL語句全稱數據定義語句(Data Define Language)。用於定義數據的結構或Schema,如:CREATE、ALTER、DROP、TRUNCATE、COMMENT、RENAME。當我們在執行某個存儲過程、或者編譯它的時候Oracle會自動給這個對象加上DDL鎖,同時也會對這個存儲過程所引用的對象加鎖。
瞭解了以上知識以後,我們可以得出結論:編譯包長時間無響應說明產生了死鎖。我們可以輕易的讓這種死鎖發生,舉例:

打開一個PL/SQL,開始調試某個函數(假設爲:FUN_CORE_SERVICECALL),並保持在調試狀態


打開一個SQL Window,輸入Select *From dba_ddl_locks aWhere a.name ='FUN_CORE_SERVICECALL'會發現一行記錄:


打開一個新的PL/SQL,重新編譯這個函數。我們會發現此時已經無法響應了


回到第一個PL/SQL ,重新執行Select *From dba_ddl_locks aWhere a.name ='FUN_CORE_SERVICECALL'我們將會看到如下記錄:

上述的情況表明發生了鎖等待的情況。

在Oracle中DDL鎖分爲:Exclusive DDL Locks(排他的DDL)、Share DDL Locks(共享DDL鎖)、Breakable Parse Locks(可被打破的解析鎖)幾類。篇幅所限,這裏就不再詳細介紹了。根據這個例子推理一下,當我們試圖編譯、修改存儲過程、函數、包等對數據對象的時候,如果別人也正在編譯或修改他們時就會產生鎖等待;或者我們在編譯某個存儲過程的時候,如果它所引用的數據庫對象正在被修改應該也會產生鎖等待。這種假設有興趣的兄弟可以測試下,不過比較困難。

解決方案

碰到這種問題,如果知道是被誰鎖定了(可以查出來的),可以讓對方儘快把鎖釋放掉;實在查不出來只能手工將這個鎖殺掉了。

死鎖是數據庫經常發生的問題,數據庫一般不會無緣無故產生死鎖,死鎖通常都是由於我們應用程序的設計本身造成的。

產生死鎖時,如何解決呢,下面是常規的解決辦法(區分是否知道誰被鎖定):

  • 知道死鎖對象
  • 不知道死鎖對象

知道死鎖對象

比如我所遇到的問題,P_REVEAL_REPORT_CLEAR_TIP編譯響應失敗。

  • 查詢V$DB_OBJECT_CACHE

    SELECT * FROM V$DB_OBJECT_CACHE WHERE name='P_REVEAL_REPORT_CLEAR_TIP' AND LOCKS!='0';

注意:P_REVEAL_REPORT_CLEAR_TIP爲存儲過程的名稱。發現locks=6,說明有6個死鎖sid

  • 按對象查出sid的值

    select /+ rule/ SID from V$ACCESS WHERE object='P_REVEAL_REPORT_CLEAR_TIP';

注意:CRM_LASTCHGINFO_DAY爲存儲過程的名稱。查詢出對應6個的sid

  • 查sid,serial#

    SELECT SID,SERIAL#,PADDR FROM V$SESSION WHERE SID='剛纔查到的SID';

  • 清除session

    alter system kill session 'sid值,serial#值' immediate;

不知道死鎖對象

  • 執行下面SQL,先查看哪些表被鎖住了

    select b.owner,b.object_name,a.session_id,a.locked_mode from v$locked_object a,dba_objects b where b.object_id = a.object_id;

  • 查處引起死鎖的會話

    select b.username,b.sid,b.serial#,logon_time from v$locked_object a,v$session b where a.session_id = b.sid order by b.logon_time;
    這裏會列出SID

  • 查出SID和SERIAL#

    SELECT SID,SERIAL#,PADDR FROM V$SESSION WHERE SID='剛纔查到的SID';

查V$SESSION視圖,這一步將得到PADDR

再次執行存儲過程,錯誤沒有了。語句執行成功!

posted @ 2019-03-11 22:17 南嶼北島 閱讀( ...) 評論( ...) 編輯 收藏
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章