informix 數據庫鎖表分析和解決方法

在聯機事務處理(OLTP)的數據庫應用系統中,多用戶、多任務的併發性是系統最重要的技術指標之一。爲了提高併發性,目前大部分RDBMS都採用加鎖技術。然而由於現實環境的複雜性,使用加鎖技術又不可避免地產生了死鎖問題。因此如何合理有效地使用加鎖技術,最小化死鎖是開發聯機事務處理系統的關鍵。          
死鎖產生的原因           
    在聯機事務處理系統中,造成死機主要有兩方面原因。一方面,由於多用戶、多任務的併發性和事務的完整性要求,當多個事務處理對多個資源同時訪問時,若雙方已鎖定一部分資源但也都需要對方已鎖定的資源時,無法在有限的時間內完全獲得所需的資源,就會處於無限的等待狀態,從而造成其對資源需求的死鎖。           
    另一方面,數據庫本身加鎖機制的實現方法不同,各數據庫系統也會產生其特殊的死鎖情況。如在Sybase      SQL Server 11中,最小鎖爲2K一頁的加鎖方法,而非行級鎖。如果某張表的記錄數少且記錄的長度較短(即記錄密度高,如應用系統中的系統配置表或系統參數表就屬於此類表),被訪問的頻率高,就容易在該頁上產生死鎖。
         
容易發生死鎖的幾種情況如下:           
1>不同的存儲過程、觸發器、動態SQL語句段按照不同的順序同時訪問多張表;              
2>在交換期間添加記錄頻繁的表,但在該表上使用了非羣集索引(non-clustered);              
3>表中的記錄少,且單條記錄較短,被訪問的頻率較高;          
4>整張表被訪問的頻率高(如代碼對照表的查詢等)。           

以上死鎖情況的對應處理方法如下: 
       
1>在系統實現時應規定所有存儲過程、觸發器、動態SQL語句段中,對多張表的操作總是使用同一順序。如:有兩個存儲過程proc1、proc2,都需要訪問三張表zltab、z2tab和z3tab,如果proc1按照zltab、z2tab和z3tab的順序進行訪問,那麼,proc2也應該按照以上順序訪問這三張表。          
2>對在交換期間添加記錄頻繁的表,使用羣集索引(clustered),以減少多個用戶添加記錄到該表的最後一頁上,在表尾產生熱點,造成死鎖。這類表多爲往來賬的流水錶,其特點是在交換期間需要在表尾追加大量的記錄,並且對已添加的記錄不做或較少做刪除操作。          
3>對單張表中記錄數不太多,且在交換期間select或updata較頻繁的表可使用設置每頁最大行的辦法,減少數據在表中存放的密度,模擬行級鎖,減少在該表上死鎖情況的發生。這類表多爲信息繁雜且記錄條數少的表。
           
如:系統配置表或系統參數表。在定義該表時添加如下語句:           
with   max_rows_per_page=1           
在存儲過程、觸發器、動態SQL語句段中,若對某些整張表select操作較頻繁,則可能在該表上與其他訪問該表的用戶產生死鎖。對於檢查賬號是否存在,但被檢查的字段在檢查期間不會被更新等非關鍵語句,可以採用在select命令中使用at       isolation       read       uncommitted子句的方法解決。該方法實際上降低了select語句對整張表的鎖級別,提高了其他用戶對該表操作的併發性。在系統高負荷運行時,該方法的效果尤爲顯著。           
如:          
select * from titles at isolation read uncommitted          
對流水號一類的順序數生成器字段,可以先執行updata流水號字段+1,然後再執行select獲取流水號的方法進行操作。

http://www.blogjava.net/imdosop/archive/2008/11/06/239085.html

 

 

數據庫鎖表的分析與解決


上面介紹了內存溢出的原因和處理方法,下面再介紹一下數據庫鎖表及阻塞的原因和處理辦法。
數據庫和操作系統一樣,是一個多用戶使用的共享資源。當多個用戶併發地存取數據時,在數據庫中就會產生多個事務同時存取同一數據的情況。若對併發操作不加控制就可能會讀取和存儲不正確的數據,破壞數據庫的一致性。加鎖是實現數據庫併發控制的一個非常重要的技術。在實際應用中經常會遇到的與鎖相關的異常情況,當兩個事務需要一組有衝突的鎖,而不能將事務繼續下去的話,就會出現死鎖,嚴重影響應用的正常執行。
在數據庫中有兩種基本的鎖類型:排它鎖(Exclusive Locks,即X鎖)和共享鎖(Share Locks,即S鎖)。當數據對象被加上排它鎖時,其他的事務不能對它讀取和修改。加了共享鎖的數據對象可以被其他事務讀取,但不能修改。數據庫利用這兩種基本的鎖類型來對數據庫的事務進行併發控制。
死鎖的第一種情況
一個用戶A 訪問表A(鎖住了表A),然後又訪問表B;另一個用戶B 訪問表B(鎖住了表B),然後企圖訪問表A;這時用戶A由於用戶B已經鎖住表B,它必須等待用戶B釋放表B才能繼續,同樣用戶B要等用戶A釋放表A才能繼續,這就死鎖就產生了。
解決方法:
這種死鎖比較常見,是由於程序的BUG產生的,除了調整的程序的邏輯沒有其它的辦法。仔細分析程序的邏輯,對於數據庫的多表操作時,儘量按照相同的順序進行處理,儘量避免同時鎖定兩個資源,如操作A和B兩張表時,總是按先A後B的順序處理, 必須同時鎖定兩個資源時,要保證在任何時刻都應該按照相同的順序來鎖定資源。
死鎖的第二種情況
用戶A查詢一條紀錄,然後修改該條紀錄;這時用戶B修改該條紀錄,這時用戶A的事務裏鎖的性質由查詢的共享鎖企圖上升到獨佔鎖,而用戶B裏的獨佔鎖由於A有共享鎖存在所以必須等A釋放掉共享鎖,而A由於B的獨佔鎖而無法上升的獨佔鎖也就不可能釋放共享鎖,於是出現了死鎖。這種死鎖比較隱蔽,但在稍大點的項目中經常發生。如在某項目中,頁面上的按鈕點擊後,沒有使按鈕立刻失效,使得用戶會多次快速點擊同一按鈕,這樣同一段代碼對數據庫同一條記錄進行多次操作,很容易就出現這種死鎖的情況。
解決方法:
1、對於按鈕等控件,點擊後使其立刻失效,不讓用戶重複點擊,避免對同時對同一條記錄操作。
2、使用樂觀鎖進行控制。樂觀鎖大多是基於數據版本(Version)記錄機制實現。即爲數據增加一個版本標識,在基於數據庫表的版本解決方案中,一般是通過爲數據庫表增加一個“version”字段來實現。讀取出數據時,將此版本號一同讀出,之後更新時,對此版本號加一。此時,將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,如果提交的數據版本號大於數據庫表當前版本號,則予以更新,否則認爲是過期數據。樂觀鎖機制避免了長事務中的數據庫加鎖開銷(用戶A和用戶B操作過程中,都沒有對數據庫數據加鎖),大大提升了大併發量下的系統整體性能表現。Hibernate 在其數據訪問引擎中內置了樂觀鎖實現。需要注意的是,由於樂觀鎖機制是在我們的系統中實現,來自外部系統的用戶更新操作不受我們系統的控制,因此可能會造成髒數據被更新到數據庫中。
3、使用悲觀鎖進行控制。悲觀鎖大多數情況下依靠數據庫的鎖機制實現,如Oracle的Select … for update語句,以保證操作最大程度的獨佔性。但隨之而來的就是數據庫性能的大量開銷,特別是對長事務而言,這樣的開銷往往無法承受。如一個金融系統,當某個操作員讀取用戶的數據,並在讀出的用戶數據的基礎上進行修改時(如更改用戶賬戶餘額),如果採用悲觀鎖機制,也就意味着整個操作過程中(從操作員讀出數據、開始修改直至提交修改結果的全過程,甚至還包括操作員中途去煮咖啡的時間),數據庫記錄始終處於加鎖狀態,可以想見,如果面對成百上千個併發,這樣的情況將導致災難性的後果。所以,採用悲觀鎖進行控制時一定要考慮清楚。
死鎖的第三種情況
如果在事務中執行了一條不滿足條件的update語句,則執行全表掃描,把行級鎖上升爲表級鎖,多個這樣的事務執行後,就很容易產生死鎖和阻塞。類似的情況還有當表中的數據量非常龐大而索引建的過少或不合適的時候,使得經常發生全表掃描,最終應用系統會越來越慢,最終發生阻塞或死鎖。
解決方法:
SQL語句中不要使用太複雜的關聯多表的查詢;使用“執行計劃”對SQL語句進行分析,對於有全表掃描的SQL語句,建立相應的索引進行優化。
5.小結
總體上來說,產生內存溢出與鎖表都是由於代碼寫的不好造成的,因此提高代碼的質量是最根本的解決辦法。有的人認爲先把功能實現,有BUG時再在測試階段進行修正,這種想法是錯誤的。正如一件產品的質量是在生產製造的過程中決定的,而不是質量檢測時決定的,軟件的質量在設計與編碼階段就已經決定了,測試只是對軟件質量的一個驗證,因爲測試不可能找出軟件中所有的BUG。
 

鎖表處理步驟:
1、onstat -ks|grep HDR+X   //查詢是那個表被鎖
address  wtlist   owner  lklist   type    tblsnum  rowid    key#/bsiz
c1809510   0     d656e774 c181cb3c HDR+X    6002e1   2c602       0

需要關注lklist和type項,從上面來看tblsnum爲6002e1(6292193十六進制轉換成十進制)的表被鎖了。可以重查詢是那個表被鎖:

dbaccess :select * from systables where partnum='6292193'得到
tabname    basetab_mvpn
owner      smpmml
partnum    6292193
tabid      12813
rowsize    464
ncols      61
nindexes   1
nrows      2984
created    12/10/2002
version    839843846
tabtype    T
locklevel  R
npused     746
fextsize   16
nextsize   16
flags      0

2、onstat -u,將owner(address)爲d656e774的線程找出來

address  flags  sessid   user  tty   wait  tout locks nreads   nwrites
d656e774 Y--P--- 4261   smp20  -   d6ad2330 0    180   99620    16

3、onstat -g sql d656e774可以將這個線程執行過的sql語句打印出來。
4、只要用informix用戶執行onmode-z sessid幹掉線程
onmode-z 4261

重點說明:onstat -g ses sessid找個進程PID來,然後ps -ef|grep Pid; kill -9 pid
在處理這些問題時還會遇到表被鎖是因爲該線程還沒有執行完畢,此時就不能簡單的 onmode -z殺線程

sylar MAIL: [email protected]
發佈了7 篇原創文章 · 獲贊 0 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章