在通過JDBC對數據庫進行并發訪問時,為了解決并發之間的鎖的控制,JDBC提供了一個隔離級別(Isolation)的方式解決并發訪問的問題。
因為最近時間在解決公司工作流在客戶現場的高并發情況下經常出現死鎖(dead lock)或者事務超時情況,而工作流的應用大多數主要這幾種業務:查詢工作項、領取工作、完成(或者提交)工作。根據以前公司在其他應用中并沒有出現這 些故障,對所上線的環境進行的分析,主要差異是出問題的系統是DB2(其他系統基本上是ORACLE)。針對上面的問題進行了下面分析。
數據庫的差異性:
- ORACLE數據庫比較強調能夠支持高并發情況下的訪問,保證能夠“讀”到數據
- DB2數據庫比較強調高并發情況下的數據完整性,保證能夠“讀一致”的數據
數據庫的隔離級別分析:
- ORACLE缺省使用的是CS級別的數據庫訪問
- DB2缺省使用的是RS級別的數據庫訪問
JDBC的數據隔離級別設置:
JDBC
|
數據庫隔離級別
|
數據訪問情況
|
TRANSACTION_READ_UNCOMMITTED
|
ur
|
就是俗稱“臟讀”(dirty read),在沒有提交數據時能夠讀到已經更新的數據
|
TRANSACTION_READ_COMMITTED
|
cs
|
在一個事務中進行查詢時,允許讀取提交前的數據,數據提交后,當前查詢就可以讀取到數據。update數據時候并不鎖住表
|
TRANSACTION_REPEATABLE_READ
|
rs
|
在一個事務中進行查詢時,不允許讀取其他事務update的數據,允許讀取到其他事務提交的新增數據
|
TRANSACTION_SERIALIZABLE
|
rr
|
在一個事務中進行查詢時,不允許任何對這個查詢表的數據修改。
|
在websphere環境中通過JDBC連接DB2數據庫的隔離級別的情況:
- 只有使用實體(Entity Bean)的情況下可以通過修改ibm的部署文件修改當前數據操作的隔離級別
- 如果通過session bean,jdbc connection方式獲得的數據庫連接缺省都是TRANSACTION_REPEATABLE_READ
- 在通過數據源獲得DB2的數據庫連接時候,DB2的JDBC driver會缺省修改當前連接的隔離級別到TRANSACTION_REPEATABLE_READ
工作流應用分析:
在工作流應用中,主要是這些操作:查詢工作項、領取工作、完成(或者提交)工作。
- 查詢工作項,數據庫操作是select,在工作流應用中的操作頻率最高
- 領取工作,數據庫操作是update,在工作流應用中的操作頻率較低
- 完成(或者提交)工作,數據庫操作主要是update、insert,在工作流應用中的操作頻率較低
根據上面的應用分析,工作流應用中大量的select,少量對數據進行update,而發生死鎖(dead lock)的現象是在DB2,在ORACLE上基本沒有發生的主要原因是:
在DB2中數據庫的隔離級別是rs,如果系統中有大量的select,特別是如果查詢的工作項特別多,查詢慢的情況下,造成其他的update操作等待select請求結束,如果又有大量的select請求過來時會出現死鎖(dead lock)
解決方案:
首先考慮參照ORACLE的模式,將DB2的JDBC連接的隔離級別改為TRANSACTION_READ_COMMITTED,來提高并發能力,通常情況下獲取數據庫連接都是通過一個方法獲得,因此可以直接改這個方法獲得連接后直接將JDBC的Connection的隔離級別調整為TRANSACTION_READ_COMMITTED。但是在驗證這種方案是發現了一個問題:
如果在一個JTA事務中,先獲取一個JDBC的Connection,程序修改了隔離級別到TRANSACTION_READ_COMMITTED, 在這個Connection中進行了數據庫操作,然后關閉連接,再從數據源中獲取Connection時候,DB2的Driver會修改當前的連接的隔離 級別,造成了在一個全局事務中修改了隔離級別,系統會自動拋出異常。
上面的方案中是對任何一個Connection都修改隔離級別,因為存在問題,因此只能對單個數 據庫請求進行隔離級別的操作。對所有的獲取工作項的select查詢語句之后添加上 with cs的方式,這樣這條查詢請求并不會鎖住表,其他update操作就能夠正常工作。