【轉】資料庫隔離等級詳解----學習筆記

來源:互聯網
上載者:User

標籤:

http://blog.sina.com.cn/s/blog_616b428f010163bo.html

事務(transaction)是資料庫管理系統的執行單位,可以是一個資料庫操作(如Select操作)或者是一組操作序列。事務ACID屬性,即原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)。

原子性:保證事務中的所有操作全部執行或全部不執行。例如執行轉賬事務,要麼轉賬成功,要麼失敗。成功,則金額從轉出帳戶轉入到目的帳戶,並且兩個帳戶金額將發生相應的變化;失敗,則兩個賬戶的金額都不變。不會出現轉出帳戶扣了錢,而目的帳戶沒有收到錢的情況。

一致性:保證資料庫始終保持資料的一致性——事務操作之前是一致的,事務操作之後也是一致的,不管事務成功與否。如上面的例子,轉賬之前和之後資料庫都保持資料上的一致性。

隔 離性:多個事務並發執行的話,結果應該與多個事務串列執行效果是一樣的。顯然最簡單的隔離就是將所有事務都串列執行:先來先執行,一個事務執行完了才允許 執行下一個。但這樣資料庫的效率低下,如:兩個不同的事務只是讀取同一批資料,這樣完全可以並發進行。為了控制並發執行的效果就有了不同的隔離等級。下面 將詳細介紹。

持久性:持久性表示事物操作完成之後,對資料庫的影響是持久的,即使資料庫因故障而受到破壞,資料庫也應該能夠恢複。通常的實現方式是採用日誌。

 

交易隔離等級(transaction isolation levels):隔離等級就是對對事務並發控制的等級。ANSI/ ISO SQL將其分為序列化(SERIALIZABLE)、可重複讀(REPEATABLE READ)、讀已提交(READ COMMITED)、讀未提交(READ UNCOMMITED)四個等級。為了實現隔離等級通常資料庫採用鎖(Lock)。一般在編程的時候只需要設定隔離等級,至於具體採用什麼鎖則由資料庫來設定。首先介紹四種等級,然後舉例解釋後面三個等級(可重複讀、讀已提交、讀未提交)中會出現的並發問題。

序列化(SERIALIZABLE):所有事務都一個接一個地串列執行,這樣可以避免幻讀(phantom reads)。對於基於鎖來實現並發控制的資料庫來說,序列化要求在執行範圍查詢(如選取年齡在10到30之間的使用者)的時候,需要擷取範圍鎖(range lock)。如果不是基於鎖實現並發控制的資料庫,則檢查到有違反串列操作的事務時,需要滾回該事務。

可重複讀(REPEATABLE READ):所有被Select擷取的資料都不能被修改,這樣就可以避免一個事務前後讀取資料不一致的情況。但是卻沒有辦法控制幻讀,因為這個時候其他事務不能更改所選的資料,但是可以增加資料,因為前一個事務沒有範圍鎖。

讀已提交(READ COMMITED):被讀取的資料可以被其他事務修改。這樣就可能導致不可重複讀取。也就是說,事務的讀取資料的時候擷取讀鎖,但是讀完之後立即釋放(不需要等到事務結束),而寫鎖則是事務提交之後才釋放。釋放讀鎖之後,就可能被其他事物修改資料。該等級也是SQL Server預設的隔離等級。

讀未提交(READ UNCOMMITED):這是最低的隔離等級,允許其他事務看到沒有提交的資料。這種等級會導致髒讀(Dirty Read)。

 

       例子:下面考察後面三種隔離等級對應的並發問題。假設有兩個事務。事務1執行查詢1,然後事務2執行查詢2,然後提交,接下來事務1中的查詢1再執行一次。查詢基於以下表進行:

users

id

name

age

1

Joe

20

2

Jill

25

可重複讀(幻讀,phantom reads)

一個事務中先後各執行一次同一個查詢,但是返回的結果集卻不一樣。發生這種情況是因為在執行Select操作的時候沒有擷取範圍鎖(Range Lock),導致其他事務仍然可以插入新的資料。

Transaction 1

Transaction 2

 

SELECT * FROM users

WHERE age BETWEEN 10 AND 30;

 

 

 

INSERT INTO users VALUES ( 3, ‘Bob‘, 27 );

COMMIT;

 

SELECT * FROM users

WHERE age BETWEEN 10 AND 30;

 

注意transaction 1對同一個查詢語句(Query 1)執行了兩次。 如果採用更進階別的隔離等級(即序列化)的話,那麼前後兩次查詢應該返回同樣的結果集。但是在可重複讀隔離等級中卻前後兩次結果集不一樣。但是為什麼叫做可重複讀等級呢?那是因為該等級解決了下面的不可重複讀取問題。

讀已提交(不可重複讀取,Non-repeatable reads)

在採用鎖來實現並發控制的資料庫系統中,不可重複讀取是因為在執行Select操作的時候沒有加讀鎖(read lock)。

Transaction 1

Transaction 2

 

SELECT * FROM users WHERE id = 1;

 

 

 

UPDATE users SET age = 21 WHERE id = 1;

COMMIT;

 

SELECT * FROM users WHERE id = 1;

 

在這個例子當中,Transaction 2提交成功,所以Transaction 1第二次將擷取一個不同的age 值.在SERIALIZABLE和REPEATABLE READ隔離等級中,資料庫應該返回同一個值。而在READ COMMITTED和READ UNCOMMITTED層級中資料庫返回更新的值。這樣就出現了不可重複讀取。

讀未提交 (髒讀,dirty reads)

如果一個事務2讀取了另一個事務1修改的值,但是最後事務1滾回了,那麼事務2就讀取了一個髒資料,這也就是所謂的髒讀。發生這種情況就是允許事務讀取未提交的更新。

Transaction 1

Transaction 2

 

SELECT * FROM users WHERE id = 1;

 

 

 

UPDATE users SET age = 21 WHERE id = 1;

 

SELECT * FROM users WHERE id = 1;

 

 

RollBack

 

綜上述,可以等到下面的表格:

隔離等級

髒讀

不可重複讀取

幻讀

讀未提交

YES

YES

YES

讀已提交

NO

YES

YES

可重複讀

NO

NO

YES

序列化

NO

NO

NO

 

  參考:

Isolation (database systems):http://en.wikipedia.org/wiki/Isolation_(computer_science)

 

共用鎖定:由讀表操作加上的鎖,加鎖後其他使用者只能擷取該表或行的共用鎖定,不能擷取排它鎖,也就是說只能讀不能寫

排它鎖:由寫表操作加上的鎖,加鎖後其他使用者不能擷取該表或行的任何鎖,典型是mysql事務中的

鎖的範圍:

行鎖: 對某行記錄加上鎖

表鎖: 對整個表加上鎖

這樣組合起來就有,行級共用鎖定,表級共用鎖定,行級獨佔鎖定,表級獨佔鎖定

 

start transaction;

select * from user where userId = 1 for update;

執行完這句以後

  1)當其他事務想要擷取共用鎖定,比如交易隔離等級為SERIALIZABLE的事務,執行

  select * from user;

   將會被掛起,因為SERIALIZABLE的select語句需要擷取共用鎖定

  2)當其他事務執行

  select * from user where userId = 1 for update;

  update user set userAge = 100 where userId = 1; 

  也會被掛起,因為for update會擷取這一行資料的排它鎖,需要等到前一個事務釋放該排它鎖才可以繼續進行。

http://blog.csdn.net/kingofase/article/details/5715297  中有介紹如下

在標準SQL規範中,定義了4個交易隔離等級,不同的隔離等級對事務的處理不同:

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

◆授權讀取(Read Committed):允許不可重複讀取,但不允許髒讀取。這可以通過“瞬間共用讀鎖”和“排他寫鎖”實現。讀取資料的事務允許其他事務繼續訪問該行資料,但是未提交的寫事務將會禁止其他事務訪問該行。

◆可重複讀取(Repeatable Read):禁止不可重複讀取和髒讀取,但是有時可能出現幻影資料。這可以通過“共用讀鎖”和“排他寫鎖”實現。讀取資料的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務。

◆序列化(Serializable):提供嚴格的事務隔離。它要求事務序列化執行,事務只能一個接著一個地執行,但不能並發執行。如果僅僅通過“行級鎖”是無法實現事務序列化的,必須通過其他機制保證新插入的資料不會被剛執行查詢操作的事務訪問到。

隔離等級越高,越能保證資料的完整性和一致性,但是對並發效能的影響也越大。對於多 數應用程式,可以優先考慮把資料庫系統的隔離等級設為Read Committed,它能夠避免髒讀取,而且具有較好的並發效能。儘管它會導致不可重複讀取、虛讀和第二類丟失更新這些並發問題,在可能出現這類問題的個別 場合,可以由應用程式採用悲觀鎖或樂觀鎖來控制。

通過前面的介紹已經知道,通過選用不同的隔離等級就可以在不同程度上避免前面所提及的在交易處理中所面臨的各種問題。所以,資料庫隔離等級的選取就顯得尤為重要,在選取資料庫的隔離等級時,應該注意以下幾個處理的原則:

首先,必須排除“未授權讀取”,因為在多個事務之間使用它將會是非常危險的。事務的 復原操作或失敗將會影響到其他並發事務。第一個事務的復原將會完全將其他事務的操作清除,甚至使資料庫處在一個不一致的狀態。很可能一個已復原為結束的事 務對資料的修改最後卻修改提交了,因為“未授權讀取”允許其他事務讀取資料,最後整個錯誤狀態在其他事務之間傳播開來。

其次,絕大部分應用都無須使用“序列化”隔離(一般來說,讀取幻影資料並不是一個問題),此隔離等級也難以測量。目前使用序列化隔離的應用中,一般都使用悲觀鎖,這樣強行使所有事務都序列化執行。

剩下的也就是在“授權讀取”和“可重複讀取”之間選擇了。我們先考慮可重複讀取。如 果所有的資料訪問都是在統一的原子資料庫事務中,此隔離等級將消除一個事務在另外一個並發事務過程中覆蓋資料的可能性(第二個事務更新丟失問題)。這是一 個非常重要的問題,但是使用可重複讀取並不是解決問題的唯一途徑。

假設使用了“版本資料”,Hibernate會自動使用版本資料。 Hibernate的一級Session緩衝和版本資料已經為你提供了“可重複讀取隔離”絕大部分的特性。特別是,版本資料可以防止二次更新丟失的問題, 一級Session緩衝可以保證持久載入資料的狀態與其他事務對資料的修改隔離開來,因此如果使用對所有的資料庫事務採用授權讀取隔離和版本資料是行得通 的。

“可重複讀取”為資料庫查詢提供了更好的效率(僅對那些長時間的資料庫事務),但是由於幻影讀取依然存在,因此沒必要使用它(對於Web應用來說,一般也很少在一個資料庫事務中對同一個表查詢兩次)。

也可以同時考慮選擇使用Hibernate的二級緩衝,它可以如同底層的資料庫事務 一樣提供相同的事務隔離,但是它可能弱化隔離。假如在二級緩衝大量使用緩衝並發策略,它並不提供重複讀取語義(例如,後面章節中將要討論的讀寫,特別是非 嚴格讀寫),很容易可以選擇預設的隔離等級:因為無論如何都無法實現“可重複讀取”,因此就更沒有必要拖慢資料庫了。另一方面,可能對關鍵類不採用二級緩 存,或者採用一個完全的事務緩衝,提供“可重複讀取隔離”。那麼在業務中需要使用到“可重複讀取”嗎?如果你喜歡,當然可以那樣做,但更多的時候並沒有必 要花費這個代價。

【轉】資料庫隔離等級詳解----學習筆記

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.