1定義編輯
2問題的提出編輯
更新丟失
髒讀
不可重複讀
3解決方案編輯
未受權讀取
受權讀取
可重複讀取(Repeatable Read)
序列化(Serializable)
ORACLE數據庫事務隔離級別介紹
兩個併發事務同時訪問數據庫表相同的行時,可能存在如下三個問題:
一、幻想讀:事務T1讀取一條指定where條件的語句,返回結果集。此時事務T2插入一行新記錄,剛好知足T1的where條件。而後T1使用相同的條件再次查詢,結果集中能夠看到T2插入的記錄,這條新紀錄就是幻想。
二、不可重複讀取:事務T1讀取一行記錄,緊接着事務T2修改了T1剛剛讀取的記錄,而後T1再次查詢,發現與第一次讀取的記錄不一樣,這稱爲不可重複讀。
三、髒讀:事務T1更新了一行記錄,還未提交所作的修改,這個T2讀取了更新後的數據,而後T1執行回滾操做,取消剛纔的修改,因此T2所讀取的行就無效,也就是髒數據。
1、爲了處理這些問題,SQL標準定義瞭如下幾種事務隔離級別:
READ UNCOMMITTED 幻想讀、不可重複讀和髒讀都容許。一個會話能夠讀取其餘事務未提交的更新結果,若是這個事務最後以回滾結束,這時的讀取結果就多是不正確的,因此多數的數據庫都不會運用這種隔離級別。
READ COMMITTED 容許幻想讀、不可重複讀,不容許髒讀。一個會話只能讀取其餘事務已提交的更新結果,不然,發生等待,可是其餘會話能夠修改這個事務中被讀取的記錄,而沒必要等待事務結束,顯然,在這種隔離級別下,一個事務中的兩個相同的讀取操做,其結果可能不一樣。
REPEATABLE READ 容許幻想讀,不容許不可重複讀和髒讀。在一個事務中,若是在兩次相同條件的讀取操做之間沒有添加記錄的操做,也沒有其餘更新操做致使在這個查詢條件下記錄數增多,則兩次讀取結果相同。換句話說,就是在一個事務中第一次讀取的記錄保證不會在這個事務期間發生改動。SQL Server是經過在整個事務期間給讀取的記錄加鎖實現這種隔離級別的,這樣,在這個事務結束前,其餘會話不能修改事務中讀取的記錄,而只能等待事務結束,可是SQL Server不會阻礙其餘會話向表中添加記錄,也不阻礙其餘會話修改其餘記錄。
SERIALIZABLE 幻想讀、不可重複讀和髒讀都不容許。在一個事務中,讀取操做的結果是在這個事務開始以前其餘事務就已經提交的記錄,SQL Server經過在整個事務期間給表加鎖實現這種隔離級別。在這種隔離級別下,對這個表的全部DML操做都是不容許的,即要等待事務結束,這樣就保證了在一個事務中的兩次讀取操做的結果確定是相同的。SQL標準所定義的默認事務隔離級別是SERIALIZABLE。
2、Oracle中的隔離級別及實現機制:
Oracle數據庫支持READ COMMITTED 和 SERIALIZABLE這兩種事務隔離級別。因此Oracle不支持髒讀,即Oracle中不容許一個會話讀取其餘事務未提交的數據修改結果,從而防止了因爲事務回滾發生的讀取不正確。
Oracle回滾段,在修改數據記錄時,會把這些記錄被修改以前的結果存入回滾段或撤銷段中。Oracle讀取操做不會阻礙更新操做,更新操做也不會阻礙讀取操做,這樣在Oracle中的各類隔離級別下,讀取操做都不會等待更新事務結束,更新操做也不會由於另外一個事務中的讀取操做而發生等待,這也是Oracle事務處理的一個優點所在。
Oracle缺省的配置是Read Committed隔離級別(也稱爲語句級別的隔離),在這種隔離級別下,若是一個事務正在對某個表執行 DML操做,而這時另一個會話對這個表的記錄執行讀取操做,則Oracle會去讀取回滾段或撤銷段中存放的更新以前的記錄,而不會象SQL Server同樣等待更新事務的結束。
Oracle的Serializable隔離級別(也稱爲事務級別的隔離),事務中的讀取操做只能讀取這個事務開始以前已經提交的數據結果。若是在讀取時,其餘事務正在對記錄執行修改,則Oracle就會在回滾段或撤銷段中去尋找對應的原來未經修改的記錄(並且是在讀取操做所在的事務開始以前存放於回滾段或撤銷段的記錄),這時讀取操做也不會由於相應記錄被更新而等待。
設置隔離級別使用 SET TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
下面是oracle 設置SERIALIZABLE隔離級別一個示例:
左面是事務T1,右面是事務T2,由於T2級別爲SERIALIZABLE,因此即便事務T1在提交了數據以後,事務T2仍是看不到T1提交的數據,幻想讀和不可重複讀都不容許了。
那如何能查看到T1新增的記錄呢? 上面T1和T2是併發執行,在T1執行insert的時候事務T2已經開始了,由於T2級別是SERIALIZABLE,因此T2所查詢的數據集是T2事務開始前數據庫的數據。即事務T1在事務T2開始以後的insert和update操做的影響都不會影響事務T2。如今從新開啓一個事務T3 就能夠看到T1新增的記錄了。
當下列事件發生時,事務就開始了:
一、鏈接到數據庫,並執行第一條DML語句
二、前一個事務結束後,又輸入了另外一條DML語句
兩個併發事務同時訪問數據庫表相同的行時,可能存在如下三個問題:
一、幻想讀:事務T1讀取一條指定where條件的語句,返回結果集。此時事務T2插入一行新記錄,剛好知足T1的where條件。而後T1使用相同的條件再次查詢,結果集中能夠看到T2插入的記錄,這條新紀錄就是幻想。
二、不可重複讀取:事務T1讀取一行記錄,緊接着事務T2修改了T1剛剛讀取的記錄,而後T1再次查詢,發現與第一次讀取的記錄不一樣,這稱爲不可重複讀。
三、髒讀:事務T1更新了一行記錄,還未提交所作的修改,這個T2讀取了更新後的數據,而後T1執行回滾操做,取消剛纔的修改,因此T2所讀取的行就無效,也就是髒數據。
1、爲了處理這些問題,SQL標準定義瞭如下幾種事務隔離級別:
READ UNCOMMITTED 幻想讀、不可重複讀和髒讀都容許。一個會話能夠讀取其餘事務未提交的更新結果,若是這個事務最後以回滾結束,這時的讀取結果就多是不正確的,因此多數的數據庫都不會運用這種隔離級別。
READ COMMITTED 容許幻想讀、不可重複讀,不容許髒讀。一個會話只能讀取其餘事務已提交的更新結果,不然,發生等待,可是其餘會話能夠修改這個事務中被讀取的記錄,而沒必要等待事務結束,顯然,在這種隔離級別下,一個事務中的兩個相同的讀取操做,其結果可能不一樣。
REPEATABLE READ 容許幻想讀,不容許不可重複讀和髒讀。在一個事務中,若是在兩次相同條件的讀取操做之間沒有添加記錄的操做,也沒有其餘更新操做致使在這個查詢條件下記錄數增多,則兩次讀取結果相同。換句話說,就是在一個事務中第一次讀取的記錄保證不會在這個事務期間發生改動。SQL Server是經過在整個事務期間給讀取的記錄加鎖實現這種隔離級別的,這樣,在這個事務結束前,其餘會話不能修改事務中讀取的記錄,而只能等待事務結束,可是SQL Server不會阻礙其餘會話向表中添加記錄,也不阻礙其餘會話修改其餘記錄。
SERIALIZABLE 幻想讀、不可重複讀和髒讀都不容許。在一個事務中,讀取操做的結果是在這個事務開始以前其餘事務就已經提交的記錄,SQL Server經過在整個事務期間給表加鎖實現這種隔離級別。在這種隔離級別下,對這個表的全部DML操做都是不容許的,即要等待事務結束,這樣就保證了在一個事務中的兩次讀取操做的結果確定是相同的。SQL標準所定義的默認事務隔離級別是SERIALIZABLE。
2、Oracle中的隔離級別及實現機制:
Oracle數據庫支持READ COMMITTED 和 SERIALIZABLE這兩種事務隔離級別。因此Oracle不支持髒讀,即Oracle中不容許一個會話讀取其餘事務未提交的數據修改結果,從而防止了因爲事務回滾發生的讀取不正確。
Oracle回滾段,在修改數據記錄時,會把這些記錄被修改以前的結果存入回滾段或撤銷段中。Oracle讀取操做不會阻礙更新操做,更新操做也不會阻礙讀取操做,這樣在Oracle中的各類隔離級別下,讀取操做都不會等待更新事務結束,更新操做也不會由於另外一個事務中的讀取操做而發生等待,這也是Oracle事務處理的一個優點所在。
Oracle缺省的配置是Read Committed隔離級別(也稱爲語句級別的隔離),在這種隔離級別下,若是一個事務正在對某個表執行 DML操做,而這時另一個會話對這個表的記錄執行讀取操做,則Oracle會去讀取回滾段或撤銷段中存放的更新以前的記錄,而不會象SQL Server同樣等待更新事務的結束。
Oracle的Serializable隔離級別(也稱爲事務級別的隔離),事務中的讀取操做只能讀取這個事務開始以前已經提交的數據結果。若是在讀取時,其餘事務正在對記錄執行修改,則Oracle就會在回滾段或撤銷段中去尋找對應的原來未經修改的記錄(並且是在讀取操做所在的事務開始以前存放於回滾段或撤銷段的記錄),這時讀取操做也不會由於相應記錄被更新而等待。
設置隔離級別使用 SET TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
下面是oracle 設置SERIALIZABLE隔離級別一個示例:
左面是事務T1,右面是事務T2,由於T2級別爲SERIALIZABLE,因此即便事務T1在提交了數據以後,事務T2仍是看不到T1提交的數據,幻想讀和不可重複讀都不容許了。
那如何能查看到T1新增的記錄呢? 上面T1和T2是併發執行,在T1執行insert的時候事務T2已經開始了,由於T2級別是SERIALIZABLE,因此T2所查詢的數據集是T2事務開始前數據庫的數據。即事務T1在事務T2開始以後的insert和update操做的影響都不會影響事務T2。如今從新開啓一個事務T3 就能夠看到T1新增的記錄了。
當下列事件發生時,事務就開始了:
一、鏈接到數據庫,並執行第一條DML語句
二、前一個事務結束後,又輸入了另外一條DML語句
http://blog.csdn.Net/jialinqiang/article/details/8723044
1.數據庫事務的概念:
•事務是指一組相互依賴的操做行爲,如銀行交易、股票交易或網上購物。事務的成功取決於這些相互依賴的操做行爲是否都能執行成功,只要有一個操做行爲失敗,就意味着整個事務失敗。例如,Tom到銀行辦理轉帳事務,把100元錢轉到Jack的帳號上,這個事務包含如下操做行爲:
–(1)從Tom的帳戶上減去100元。
–(2)往Jack的帳戶上增長100元。
•顯然,以上兩個操做必須做爲一個不可分割的工做單元。假如僅僅第一步操做執行成功,使得Tom的帳戶上扣除了100元,可是第二步操做執行失敗,Jack的帳戶上沒有增長100元,那麼整個事務失敗。
•數據庫事務是對現實生活中事務的模擬,它由一組在業務邏輯上相互依賴的SQL語句組成。
2.數據庫事務的生命週期:
3.聲明事務的邊界:
•事務的開始邊界。
•事務的正常結束邊界(COMMIT):提交事務,永久保存被事務更新後的數據庫狀態。
•事務的異常結束邊界(ROLLBACK):撤銷事務,使數據庫退回到執行事務前的初始狀態。
(1).在mysql.exe中聲明事務:
•每啓動一個mysql.exe程序,就會獲得一個單獨的數據庫鏈接。每一個數據庫鏈接都有個全局變量@@autocommit,表示當前的事務模式,它有兩個可選值:
–0:表示手工提交模式。
–1:默認值,表示自動提交模式。
•若是要察看當前的事務模式,可以使用以下SQL命令:
–mysql> select @@autocommit
•若是要把當前的事務模式改成手工提交模式,可以使用以下SQL命令:
–mysql> set autocommit=0;
——在自動提交模式下提交事務:
•在自動提交模式下,每一個SQL語句都是一個獨立的事務。若是在一個mysql.exe程序中執行SQL語句:
–mysql>insert into ACCOUNTS values(1,'Tom',1000);
•MySQL會自動提交這個事務,這意味着向ACCOUNTS表中新插入的記錄會永久保存在數據庫中。此時在另外一個mysql.exe程序中執行SQL語句:
–mysql>select * from ACCOUNTS;
•這條select語句會查詢到ID爲1的ACCOUNTS記錄。這代表在第一個mysql.exe程序中插入的ACCOUNTS記錄被永久保存,這體現了事務的ACID特性中的持久性。
——在手工模式下提交事務:
•在手工提交模式下,必須顯式指定事務開始邊界和結束邊界:
–事務的開始邊界:begin
–提交事務:commit
–撤銷事務:rollback
例:
–mysql>begin;
–mysql>select * from ACCOUNTS;
–mysql>commit;
(2).經過JDBC API聲明事務邊界:
• Connection提供瞭如下用於控制事務的方法:
–setAutoCommit(boolean autoCommit):設置是否自動提交事務
–commit():提交事務
–rollback():撤銷事務
例:
try {
con = Java.sql.DriverManager.getConnection(dbUrl,dbUser,dbPwd);
//設置手工提交事務模式
con.setAutoCommit(false);
stmt = con.createStatement();
//數據庫更新操做1
stmt.executeUpdate("update ACCOUNTS set BALANCE=900 where ID=1 ");
//數據庫更新操做2
stmt.executeUpdate("update ACCOUNTS set BALANCE=1000 where ID=2 ");
con.commit(); //提交事務
}catch(Exception e) {
try{
con.rollback(); //操做不成功則撤銷事務
}catch(Exception ex){
//處理異常
……
}
//處理異常
……
}finally{…}
(3).經過hibernate API聲明事務邊界:
•聲明事務的開始邊界:Transaction tx=session.beginTransaction();
•提交事務: tx.commit();
•撤銷事務: tx.rollback();
4.多個事務併發時的併發問題:
•第一類丟失更新:撤銷一個事務時,把其餘事務已提交的更新數據覆蓋。
•髒讀:一個事務讀到另外一事務未提交的更新數據。
•虛讀:一個事務讀到另外一事務已提交的新插入的數據。
•不可重複讀:一個事務讀到另外一事務已提交的更新數據。
•第二類丟失更新:這是不可重複讀中的特例,一個事務覆蓋另外一事務已提交的更新數據。
以取款事務和支票轉帳事務例:
•取款事務包含如下步驟:
–(1)某銀行客戶在銀行前臺請求取款100元,出納員先查詢帳戶信息,得知存款餘額爲1000元。
–(2)出納員判斷出存款額超過了取款額,就支付給客戶100元,並將帳戶上的存款餘額改成900元。
•支票轉帳事務包含如下步驟:
–(1)某出納員處理一轉賬支票,該支票向一賬戶匯入100元。出納員先查詢帳戶信息,得知存款餘額爲900元。
–(2)出納員將存款餘額改成1000元。
併發運行的兩個事務致使髒讀:
取款事務在T5時刻把存款餘額改成900元,支票轉帳事務在T6時刻查詢帳戶的存款餘額爲900元,取款事務在T7時刻被撤銷,支票轉帳事務在T8時刻把存款餘額改成1000元。
因爲支票轉帳事務查詢到了取款事務未提交的更新數據,而且在這個查詢結果的基礎上進行更新操做,若是取款事務最後被撤銷,會致使銀行客戶損失100元。
併發運行的兩個事務致使第二類更新丟失:
取款事務在T5時刻根據在T3時刻的查詢結果,把存款餘額改成1000-100元,在T6時刻提交事務。支票轉帳事務在T7時刻根據在T4時刻的查詢結果,把存款餘額改成1000+100
元。因爲支票轉帳事務覆蓋了取款事務對存款餘額所作的更新,致使銀行最後損失100元。
5.數據庫的隔離級別:
(1).隔離級別與併發性能的關係:
(2).設置隔離級別的原則:
•隔離級別越高,越能保證數據的完整性和一致性,可是對併發性能的影響也越大。
•對於多數應用程序,能夠優先考慮把數據庫系統的隔離級別設爲Read Committed,它可以避免髒讀,並且具備較好的併發性能。儘管它會致使不可重複讀、虛讀
和第二類丟失更新這些併發問題,在可能出現這類問題的個別場合,能夠由應用程序採用悲觀鎖或樂觀鎖來控制。
(3)在mysql.exe程序中中設置隔離級別:
•每啓動一個mysql.exe程序,就會得到一個單獨的數據庫鏈接。每一個數據庫鏈接都有個全局變量@@tx_isolation,表示當前的事務隔離級別。MySQL默認的隔離
級別爲Repeatable Read。若是要察看當前的隔離級別,可以使用以下SQL命令:
–mysql> select @@tx_isolation;
•若是要把當前mysql.exe程序的隔離級別改成Read Committed,可以使用以下SQL命令:
–mysql> set transaction isolation level read committed;
(4)在Hibernate中設置隔離級別:
•在Hibernate的配置文件中能夠顯式的設置隔離級別。每一種隔離級別都對應一個整數:
–1:Read Uncommitted
–2:Read Committed
–4:Repeatable Read
–8:Serializable•例如,如下代碼把hibernate.cfg.xml文件中的隔離級別設爲Read Committed:
hibernate.connection.isolation=2
對於從數據庫鏈接池中得到的每一個鏈接,Hibernate都會把它改成使用Read Committed隔離級別。