天天看點

資料庫事務隔離級ORACLE資料庫事務隔離級别介紹 事務隔離級别編輯 ORACLE資料庫事務隔離級别介紹

本文系轉載,原文位址:http://singo107.iteye.com/blog/1175084

√: 可能出現    ×: 不會出現

髒讀

不可重複讀

幻讀

Read uncommitted

Read committed

×

Repeatable read

Serializable

注意:我們讨論隔離級别的場景,主要是在多個事務并發的情況下,是以,接下來的講解都圍繞事務并發。

公司發工資了,上司把5000元打到singo的賬号上,但是該事務并未送出,而singo正好去檢視賬戶,發現工資已經到賬,是5000元整,非常高興。可是不幸的是,上司發現發給singo的工資金額不對,是2000元,于是迅速復原了事務,修改金額後,将事務送出,最後singo實際的工資隻有2000元,singo空歡喜一場。

資料庫事務隔離級ORACLE資料庫事務隔離級别介紹 事務隔離級别編輯 ORACLE資料庫事務隔離級别介紹

出現上述情況,即我們所說的髒讀,兩個并發的事務,“事務A:上司給singo發工資”、“事務B:singo查詢工資賬戶”,事務B讀取了事務A尚未送出的資料。

當隔離級别設定為Read uncommitted時,就可能出現髒讀,如何避免髒讀,請看下一個隔離級别。

singo拿着工資卡去消費,系統讀取到卡裡确實有2000元,而此時她的老婆也正好在網上轉賬,把singo工資卡的2000元轉到另一賬戶,并在singo之前送出了事務,當singo扣款時,系統檢查到singo的工資卡已經沒有錢,扣款失敗,singo十分納悶,明明卡裡有錢,為何......

出現上述情況,即我們所說的不可重複讀,兩個并發的事務,“事務A:singo消費”、“事務B:singo的老婆網上轉賬”,事務A事先讀取了資料,事務B緊接了更新了資料,并送出了事務,而事務A再次讀取該資料時,資料已經發生了改變。

當隔離級别設定為Read committed時,避免了髒讀,但是可能會造成不可重複讀。

當隔離級别設定為Repeatable read時,可以避免不可重複讀。當singo拿着工資卡去消費時,一旦系統開始讀取工資卡資訊(即事務開始),singo的老婆就不可能對該記錄進行修改,也就是singo的老婆不能在此時轉賬。

雖然Repeatable read避免了不可重複讀,但還有可能出現幻讀。

singo的老婆工作在銀行部門,她時常通過銀行内部系統檢視singo的信用卡消費記錄。有一天,她正在查詢到singo當月信用卡的總消費金額(select sum(amount) from transaction where month = 本月)為80元,而singo此時正好在外面胡吃海塞後在收銀台買單,消費1000元,即新增了一條1000元的消費記錄(insert transaction ... ),并送出了事務,随後singo的老婆将singo當月信用卡消費的明細列印到A4紙上,卻發現消費總額為1080元,singo的老婆很詫異,以為出現了幻覺,幻讀就這樣産生了。

Serializable是最高的事務隔離級别,同時代價也花費最高,性能很低,一般很少使用,在該級别下,事務順序執行,不僅可以避免髒讀、不可重複讀,還避免了幻像讀。

對于同時運作的多個事務, 當這些事務通路資料庫中相同的資料時, 如果沒有采取必要的隔離機制, 就會導緻各種并發問題:  

•   髒讀: 對于兩個事物 T1, T2, T1 讀取了已經被 T2 更新但還沒有被送出的字段. 之後, 若 T2 復原, T1讀取的内容就是臨時且無效的.  

•   不可重複讀: 對于兩個事物 T1, T2, T1 讀取了一個字段, 然後 T2 更新了該字段. 之後, T1再次讀取同一個字段, 值就不同了.  

•   幻讀: 對于兩個事物 T1, T2, T1 從一個表中讀取了一個字段, 然後 T2 在該表中插入了一些新的行. 之後, 如果 T1 再次讀取同一個表, 就會多出幾行.  

資料庫事務的隔離性: 資料庫系統必須具有隔離并發運作各個事務的能力, 使它們不會互相影響, 避免各種并發問題.   

一個事務與其他事務隔離的程度稱為隔離級别. 資料庫規定了多種事務隔離級别, 不同隔離級别對應不同的幹擾程度, 隔離級别越高, 資料一緻性就越好, 但并發性越弱  

資料庫提供了4中隔離級别:  

隔離級别    描述  

READ UNCOMMITTED(讀未送出資料)    允許事務讀取未被其他事務送出的變更,髒讀、不可重複讀和幻讀的問題都會出現  

READ COMMITED(讀已送出資料)   隻允許事務讀取已經被其他事務送出的變更,可以避免髒讀,但不可重複讀和幻讀問題仍然會出現  

REPEATABLE READ(可重複讀)   確定事務可以多次從一個字段中讀取相同的值,在這個事務持續期間,禁止其他事務對這個字段進行更新,可以避免髒讀和不可重複讀,但幻讀的問題依然存在  

SERIALIZABLE(串行化)   確定事務可以從一個表中讀取相同的行,在這個事務持續期間,禁止其他事務對該表執行插入、更新和删除操作,所有并發問題都可以避免,但性能十分低  

Oracle 支援的 2 種事務隔離級别:READ COMMITED, SERIALIZABLE. Oracle 預設的事務隔離級别為: READ COMMITED   

Mysql 支援 4 中事務隔離級别. Mysql 預設的事務隔離級别為: REPEATABLE READ  

本文系轉載,原文位址:http://baike.baidu.com/link?url=ECqk10IwUnE8JxHPn-qyzNsqT1XgkD6P3td71Wj3HUT-lWoJ76uU-9hSKtSuCNwp7w56IbIcQ0J00__Qn0z9ra

中文名

事務隔離級别

性    質

隔離級别

屬    性

事務

Non-repeatable Reads

<dl></dl>

<dd></dd>

在資料庫操作中,為了有效保證并發讀取資料的正确性,提出的事務隔離級别。

資料庫是要被廣大客戶所共享通路的,那麼在資料庫操作過程中很可能出現以下幾種不确定情況。

兩個事務都同時更新一行資料,一個事務對資料的更新把另一個事務對資料的更新覆寫了。這是因為系統沒有執行任何的鎖操作,是以并發事務并沒有被隔離開來。

不可重複讀(Non-repeatable Reads):一個事務對同一行資料重複讀取兩次,但是卻得到了不同的結果。

包括以下情況:

(1) 虛讀:事務T1讀取某一資料後,事務T2對其做了修改,當事務T1再次讀該資料時得到與前一次不同的值。

也稱為讀未送出(Read Uncommitted):允許髒讀取,但不允許更新丢失。如果一個事務已經開始寫資料,則另外一個事務則不允許同時進行寫操作,但允許其他事務讀此行資料。該隔離級别可以通過“排他寫鎖”實作。

兩個并發事務同時通路資料庫表相同的行時,可能存在以下三個問題:

1、幻想讀:事務T1讀取一條指定where條件的語句,傳回結果集。此時事務T2插入一行新記錄,恰好滿足T1的where條件。然後T1使用相同的條件再次查詢,結果集中可以看到T2插入的記錄,這條新紀錄就是幻想。

2、不可重複讀取:事務T1讀取一行記錄,緊接着事務T2修改了T1剛剛讀取的記錄,然後T1再次查詢,發現與第一次讀取的記錄不同,這稱為不可重複讀。

3、髒讀:事務T1更新了一行記錄,還未送出所做的修改,這個T2讀取了更新後的資料,然後T1執行復原操作,取消剛才的修改,是以T2所讀取的行就無效,也就是髒資料。

一、為了處理這些問題,SQL标準定義了以下幾種事務隔離級别:

READ UNCOMMITTED 幻想讀、不可重複讀和髒讀都允許。一個會話可以讀取其他事務未送出的更新結果,如果這個事務最後以復原結束,這時的讀取結果就可能是不正确的,是以多數的資料庫都不會運用這種隔離級别。

READ COMMITTED 允許幻想讀、不可重複讀,不允許髒讀。一個會話隻能讀取其他事務已送出的更新結果,否則,發生等待,但是其他會話可以修改這個事務中被讀取的記錄,而不必等待事務結束,顯然,在這種隔離級别下,一個事務中的兩個相同的讀取操作,其結果可能不同。

REPEATABLE READ 允許幻想讀,不允許不可重複讀和髒讀。在一個事務中,如果在兩次相同條件的讀取操作之間沒有添加記錄的操作,也沒有其他更新操作導緻在這個查詢條件下記錄數增多,則兩次讀取結果相同。換句話說,就是在一個事務中第一次讀取的記錄保證不會在這個事務期間發生改動。SQL Server是通過在整個事務期間給讀取的記錄加鎖實作這種隔離級别的,這樣,在這個事務結束前,其他會話不能修改事務中讀取的記錄,而隻能等待事務結束,但是SQL Server不會阻礙其他會話向表中添加記錄,也不阻礙其他會話修改其他記錄。

SERIALIZABLE 幻想讀、不可重複讀和髒讀都不允許。在一個事務中,讀取操作的結果是在這個事務開始之前其他事務就已經送出的記錄,SQL Server通過在整個事務期間給表加鎖實作這種隔離級别。在這種隔離級别下,對這個表的所有DML操作都是不允許的,即要等待事務結束,這樣就保證了在一個事務中的兩次讀取操作的結果肯定是相同的。SQL标準所定義的預設事務隔離級别是SERIALIZABLE。

二、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新增的記錄了。

當下列事件發生時,事務就開始了:

1、連接配接到資料庫,并執行第一條DML語句 

2、前一個事務結束後,又輸入了另一條DML語句

<a href="http://blog.csdn.net/jialinqiang/article/details/8723044" target="_blank">資料庫事務與隔離級别</a>

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&gt; select @@autocommit

•如果要把目前的事務模式改為手工送出模式,可使用如下SQL指令:

–mysql&gt; set autocommit=0;

——在自動送出模式下送出事務:

•在自動送出模式下,每個SQL語句都是一個獨立的事務。如果在一個mysql.exe程式中執行SQL語句:

–mysql&gt;insert into ACCOUNTS values(1,'Tom',1000);

•MySQL會自動送出這個事務,這意味着向ACCOUNTS表中新插入的記錄會永久儲存在資料庫中。此時在另一個mysql.exe程式中執行SQL語句:

–mysql&gt;select * from ACCOUNTS;

•這條select語句會查詢到ID為1的ACCOUNTS記錄。這表明在第一個mysql.exe程式中插入的ACCOUNTS記錄被永久儲存,這展現了事務的ACID特性中的持久性。

——在手工模式下送出事務:

•在手工送出模式下,必須顯式指定事務開始邊界和結束邊界:

–事務的開始邊界:begin

–送出事務:commit

–撤銷事務:rollback

例:

–mysql&gt;begin;

–mysql&gt;commit;

(2).通過JDBC API聲明事務邊界:

• Connection提供了以下用于控制事務的方法:

–setAutoCommit(boolean autoCommit):設定是否自動送出事務

–commit():送出事務

–rollback():撤銷事務

try {

//設定手工送出事務模式

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{…}

•聲明事務的開始邊界: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&gt; select @@tx_isolation;

•如果要把目前mysql.exe程式的隔離級别改為Read Committed,可使用如下SQL指令:

–mysql&gt; 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隔離級别。

本文轉自 張沖andy 部落格園部落格,原文連結:http://www.cnblogs.com/andy6/p/6045679.html   ,如需轉載請自行聯系原作者