Chan Chen Coding...

          數(shù)據(jù)庫事務(wù)與隔離等級詳解

          事務(wù)(transaction)是數(shù)據(jù)庫管理系統(tǒng)的執(zhí)行單位,可以是一個數(shù)據(jù)庫操作(如Select操作)或者是一組操作序列。事務(wù)ACID屬性,即原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)。

          原子性:保證事務(wù)中的所有操作全部執(zhí)行或全部不執(zhí)行。例如執(zhí)行轉(zhuǎn)賬事務(wù),要么轉(zhuǎn)賬成功,要么失敗。成功,則金額從轉(zhuǎn)出帳戶轉(zhuǎn)入到目的帳戶,并且兩個帳戶金額將發(fā)生相應(yīng)的變化;失敗,則兩個賬戶的金額都不變。不會出現(xiàn)轉(zhuǎn)出帳戶扣了錢,而目的帳戶沒有收到錢的情況。

          一致性:保證數(shù)據(jù)庫始終保持數(shù)據(jù)的一致性——事務(wù)操作之前是一致的,事務(wù)操作之后也是一致的,不管事務(wù)成功與否。如上面的例子,轉(zhuǎn)賬之前和之后數(shù)據(jù)庫都保持數(shù)據(jù)上的一致性。

          隔離性:多個事務(wù)并發(fā)執(zhí)行的話,結(jié)果應(yīng)該與多個事務(wù)串行執(zhí)行效果是一樣的。顯然最簡單的隔離就是將所有事務(wù)都串行執(zhí)行:先來先執(zhí)行,一個事務(wù)執(zhí)行完了才允許執(zhí)行下一個。但這樣數(shù)據(jù)庫的效率低下,如:兩個不同的事務(wù)只是讀取同一批數(shù)據(jù),這樣完全可以并發(fā)進行。為了控制并發(fā)執(zhí)行的效果就有了不同的隔離級別。下面將詳細介紹。

          持久性:持久性表示事物操作完成之后,對數(shù)據(jù)庫的影響是持久的,即使數(shù)據(jù)庫因故障而受到破壞,數(shù)據(jù)庫也應(yīng)該能夠恢復(fù)。通常的實現(xiàn)方式是采用日志。

           

          事務(wù)隔離級別(transaction isolation levels):隔離級別就是對對事務(wù)并發(fā)控制的等級。ANSIISOSQL將其分為串行化(SERIALIZABLE)、可重復(fù)讀(REPEATABLE READ)、讀已提交(READ COMMITED)、讀未提交(READ UNCOMMITED)四個等級。為了實現(xiàn)隔離級別通常數(shù)據(jù)庫采用鎖(Lock)。一般在編程的時候只需要設(shè)置隔離等級,至于具體采用什么鎖則由數(shù)據(jù)庫來設(shè)置。首先介紹四種等級,然后舉例解釋后面三個等級(可重復(fù)讀、讀已提交、讀未提交)中會出現(xiàn)的并發(fā)問題。

          串行化(SERIALIZABLE):所有事務(wù)都一個接一個地串行執(zhí)行,這樣可以避免幻讀(phantom reads)。對于基于鎖來實現(xiàn)并發(fā)控制的數(shù)據(jù)庫來說,串行化要求在執(zhí)行范圍查詢(如選取年齡在1030之間的用戶)的時候,需要獲取范圍鎖(range lock)。如果不是基于鎖實現(xiàn)并發(fā)控制的數(shù)據(jù)庫,則檢查到有違反串行操作的事務(wù)時,需要滾回該事務(wù)。

          可重復(fù)讀(REPEATABLE READ):所有被Select獲取的數(shù)據(jù)都不能被修改,這樣就可以避免一個事務(wù)前后讀取數(shù)據(jù)不一致的情況。但是卻沒有辦法控制幻讀,因為這個時候其他事務(wù)不能更改所選的數(shù)據(jù),但是可以增加數(shù)據(jù),因為前一個事務(wù)沒有范圍鎖。

          讀已提交(READ COMMITED):被讀取的數(shù)據(jù)可以被其他事務(wù)修改。這樣就可能導(dǎo)致不可重復(fù)讀。也就是說,事務(wù)的讀取數(shù)據(jù)的時候獲取讀鎖,但是讀完之后立即釋放(不需要等到事務(wù)結(jié)束),而寫鎖則是事務(wù)提交之后才釋放。釋放讀鎖之后,就可能被其他事物修改數(shù)據(jù)。該等級也是SQL Server默認的隔離等級。

          讀未提交(READ UNCOMMITED):這是最低的隔離等級,允許其他事務(wù)看到?jīng)]有提交的數(shù)據(jù)。這種等級會導(dǎo)致臟讀(Dirty Read)。

           

                 例子:下面考察后面三種隔離等級對應(yīng)的并發(fā)問題。假設(shè)有兩個事務(wù)。事務(wù)1執(zhí)行查詢1,然后事務(wù)2執(zhí)行查詢2,然后提交,接下來事務(wù)1中的查詢1再執(zhí)行一次。查詢基于以下表進行:

          users

          id

          name

          age

          1

          Joe

          20

          2

          Jill

          25

          可重復(fù)讀(幻讀,phantom reads)

          一個事務(wù)中先后各執(zhí)行一次同一個查詢,但是返回的結(jié)果集卻不一樣。發(fā)生這種情況是因為在執(zhí)行Select操作的時候沒有獲取范圍鎖(Range Lock),導(dǎo)致其他事務(wù)仍然可以插入新的數(shù)據(jù)。

          Transaction 1

          Transaction 2

          /* Query 1 */
          SELECT * FROM users
          WHERE age BETWEEN 10 AND 30;

           

           

          /* Query 2 */
          INSERT INTO users VALUES ( 3, 'Bob', 27 );
          COMMIT;
          /* Query 1 */
          SELECT * FROM users
          WHERE age BETWEEN 10 AND 30;

           

          注意transaction 1對同一個查詢語句(Query 1)執(zhí)行了兩次。 如果采用更高級別的隔離等級(即串行化)的話,那么前后兩次查詢應(yīng)該返回同樣的結(jié)果集。但是在可重復(fù)讀隔離等級中卻前后兩次結(jié)果集不一樣。但是為什么叫做可重復(fù)讀等級呢?那是因為該等級解決了下面的不可重復(fù)讀問題。

          讀已提交(不可重復(fù)讀,Non-repeatable reads)

          在采用鎖來實現(xiàn)并發(fā)控制的數(shù)據(jù)庫系統(tǒng)中,不可重復(fù)讀是因為在執(zhí)行Select操作的時候沒有加讀鎖(read lock)。

          Transaction 1

          Transaction 2

          /* Query 1 */
          SELECT * FROM users WHERE id = 1;

           

           

          /* Query 2 */
          UPDATE users SET age = 21 WHERE id = 1;
          COMMIT; 
          /* Query 1 */
          SELECT * FROM users WHERE id = 1;

           

          在這個例子當(dāng)中,Transaction 2提交成功,所以Transaction 1第二次將獲取一個不同的age值.在SERIALIZABLE和REPEATABLE READ隔離級別中,數(shù)據(jù)庫應(yīng)該返回同一個值。而在READ COMMITTED和READ UNCOMMITTED級別中數(shù)據(jù)庫返回更新的值。這樣就出現(xiàn)了不可重復(fù)讀。

          讀未提交 (臟讀,dirty reads)

          如果一個事務(wù)2讀取了另一個事務(wù)1修改的值,但是最后事務(wù)1滾回了,那么事務(wù)2就讀取了一個臟數(shù)據(jù),這也就是所謂的臟讀。發(fā)生這種情況就是允許事務(wù)讀取未提交的更新。

          Transaction 1

          Transaction 2

          /* Query 1 */
          SELECT * FROM users WHERE id = 1;

           

           

          /* Query 2 */
          UPDATE users SET age = 21 WHERE id = 1;
          /* Query 1 */
          SELECT * FROM users WHERE id = 1;

           

           

          RollBack

           

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

          隔離等級

          臟讀

          不可重復(fù)讀

          幻讀

          讀未提交

          YES

          YES

          YES

          讀已提交

          NO

          YES

          YES

          可重復(fù)讀

          NO

          NO

          YES

          串行化

          NO

          NO

          NO

           



          -----------------------------------------------------
          Silence, the way to avoid many problems;
          Smile, the way to solve many problems;

          posted on 2012-12-21 13:06 Chan Chen 閱讀(467) 評論(0)  編輯  收藏 所屬分類: DB

          主站蜘蛛池模板: 衡山县| 湾仔区| 舟山市| 壤塘县| 万州区| 尤溪县| 郎溪县| 辉南县| 苗栗市| 丽水市| 枣强县| 南澳县| 浦县| 德令哈市| 阳朔县| 峨山| 东乌珠穆沁旗| 建瓯市| 东平县| 景洪市| 云安县| 仁化县| 太康县| 固镇县| 宿州市| 瑞金市| 育儿| 宾川县| 鄱阳县| 凌海市| 乌恰县| 青神县| 益阳市| 芷江| 潢川县| 竹北市| 越西县| 徐水县| 平远县| 大厂| 南靖县|