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