兩個并發(fā)事務(wù)同時訪問數(shù)據(jù)庫表相同的行時,可能存在以下三個問題:
1、幻想讀:事務(wù)T1讀取一條指定where條件的語句,返回結(jié)果集。此時事務(wù)T2插入一行新記錄,恰好滿足T1的where條件。然后T1使用相同的條件再次查詢,結(jié)果集中可以看到T2插入的記錄,這條新紀錄就是幻想。
2、不可重復讀取:事務(wù)T1讀取一行記錄,緊接著事務(wù)T2修改了T1剛剛讀取的記錄,然后T1再次查詢,發(fā)現(xiàn)與第一次讀取的記錄不同,這稱為不可重復讀。
3、臟讀:事務(wù)T1更新了一行記錄,還未提交所做的修改,這個T2讀取了更新后的數(shù)據(jù),然后T1執(zhí)行回滾操作,取消剛才的修改,所以T2所讀取的行就無效,也就是臟數(shù)據(jù)。
一、為了處理這些問題,SQL標準定義了以下幾種事務(wù)隔離級別:
READ UNCOMMITTED 幻想讀、不可重復讀和臟讀都允許。一個會話可以讀取其他事務(wù)未提交的更新結(jié)果,如果這個事務(wù)最后以回滾結(jié)束,這時的讀取結(jié)果就可能是不正確的,所以多數(shù)的數(shù)據(jù)庫都不會運用這種隔離級別。
READ COMMITTED 允許幻想讀、不可重復讀,不允許臟讀。一個會話只能讀取其他事務(wù)已提交的更新結(jié)果,否則,發(fā)生等待,但是其他會話可以修改這個事務(wù)中被讀取的記錄,而不必等待事務(wù)結(jié)束,顯然,在這種隔離級別下,一個事務(wù)中的兩個相同的讀取操作,其結(jié)果可能不同。
REPEATABLE READ 允許幻想讀,不允許不可重復讀和臟讀。在一個事務(wù)中,如果在兩次相同條件的讀取操作之間沒有添加記錄的操作,也沒有其他更新操作導致在這個查詢條件下記錄數(shù)增多,則兩次讀取結(jié)果相同。換句話說,就是在一個事務(wù)中第一次讀取的記錄保證不會在這個事務(wù)期間發(fā)生改動。SQL Server是通過在整個事務(wù)期間給讀取的記錄加鎖實現(xiàn)這種隔離級別的,這樣,在這個事務(wù)結(jié)束前,其他會話不能修改事務(wù)中讀取的記錄,而只能等待事務(wù)結(jié)束,但是SQL Server不會阻礙其他會話向表中添加記錄,也不阻礙其他會話修改其他記錄。
SERIALIZABLE 幻想讀、不可重復讀和臟讀都不允許。在一個事務(wù)中,讀取操作的結(jié)果是在這個事務(wù)開始之前其他事務(wù)就已經(jīng)提交的記錄,SQL Server通過在整個事務(wù)期間給表加鎖實現(xiàn)這種隔離級別。在這種隔離級別下,對這個表的所有DML操作都是不允許的,即要等待事務(wù)結(jié)束,這樣就保證了在一個事務(wù)中的兩次讀取操作的結(jié)果肯定是相同的。SQL標準所定義的默認事務(wù)隔離級別是SERIALIZABLE。
二、Oracle中的隔離級別及實現(xiàn)機制:
Oracle數(shù)據(jù)庫支持READ COMMITTED 和 SERIALIZABLE這兩種事務(wù)隔離級別。所以O(shè)racle不支持臟讀,即Oracle中不允許一個會話讀取其他事務(wù)未提交的數(shù)據(jù)修改結(jié)果,從而防止了由于事務(wù)回滾發(fā)生的讀取不正確。
Oracle回滾段,在修改數(shù)據(jù)記錄時,會把這些記錄被修改之前的結(jié)果存入回滾段或撤銷段中。Oracle讀取操作不會阻礙更新操作,更新操作也不會阻礙讀取操作,這樣在Oracle中的各種隔離級別下,讀取操作都不會等待更新事務(wù)結(jié)束,更新操作也不會因為另一個事務(wù)中的讀取操作而發(fā)生等待,這也是Oracle事務(wù)處理的一個優(yōu)勢所在。
Oracle缺省的配置是Read Committed隔離級別(也稱為語句級別的隔離),在這種隔離級別下,如果一個事務(wù)正在對某個表執(zhí)行 DML操作,而這時另外一個會話對這個表的記錄執(zhí)行讀取操作,則Oracle會去讀取回滾段或撤銷段中存放的更新之前的記錄,而不會象SQL Server一樣等待更新事務(wù)的結(jié)束。
Oracle的Serializable隔離級別(也稱為事務(wù)級別的隔離),事務(wù)中的讀取操作只能讀取這個事務(wù)開始之前已經(jīng)提交的數(shù)據(jù)結(jié)果。如果在讀取時,其他事務(wù)正在對記錄執(zhí)行修改,則Oracle就會在回滾段或撤銷段中去尋找對應(yīng)的原來未經(jīng)修改的記錄(而且是在讀取操作所在的事務(wù)開始之前存放于回滾段或撤銷段的記錄),這時讀取操作也不會因為相應(yīng)記錄被更新而等待。
設(shè)置隔離級別使用 SET TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
下面是oracle 設(shè)置SERIALIZABLE隔離級別一個示例:
左面是事務(wù)T1,右面是事務(wù)T2,因為T2級別為SERIALIZABLE,所以即使事務(wù)T1在提交了數(shù)據(jù)之后,事務(wù)T2還是看不到T1提交的數(shù)據(jù),幻想讀和不可重復讀都不允許了。
那如何能查看到T1新增的記錄呢? 上面T1和T2是并發(fā)執(zhí)行,在T1執(zhí)行insert的時候事務(wù)T2已經(jīng)開始了,因為T2級別是SERIALIZABLE,所以T2所查詢的數(shù)據(jù)集是T2事務(wù)開始前數(shù)據(jù)庫的數(shù)據(jù)。即事務(wù)T1在事務(wù)T2開始之后的insert和update操作的影響都不會影響事務(wù)T2?,F(xiàn)在重新開啟一個事務(wù)T3 就可以看到T1新增的記錄了。
當下列事件發(fā)生時,事務(wù)就開始了:
1、連接到數(shù)據(jù)庫,并執(zhí)行第一條DML語句
2、前一個事務(wù)結(jié)束后,又輸入了另一條DML語句