MySQL的事務處理以及隔離級別

jopen 8年前發布 | 11K 次閱讀 MySQL 數據庫服務器

最近接觸了數據并發這塊,就來說下關于數據的事務處理以及隔離級別的相關知識。

1. 事務必須滿足4個條件:

原子性(Autmic):事務在執行性,要做到“要么不做,要么全做!”,就是說不允許事務部分得執行。即使因為故障而使事務不能完成,在rollback時也要消除對數據庫得影響!
一致性(Consistency):事務操作之后,數據庫所處的狀態和業務規則是一致的;比如a,b賬戶相互轉賬之后,總金額不變!
隔離性(Isolation):如果多個事務并發執行,應像各個事務獨立執行一樣!
持久性(Durability):事務提交后被持久化到數據庫.

2. MySQL處理事務有兩種方法

1) 用BEGIN,ROLLBACK,COMMIT來實現
開始:START TRANSACTION或BEGIN語句可以開始一項新的事務
提交:COMMIT可以提交當前事務,是變更成為永久變更
回滾:ROLLBACK可以回滾當前事務,取消其變更

2) 直接用set來改變mysql的自動提交模式
MYSQL默認是自動提交的,也就是你提交一個QUERY,它就直接執行!
我們可以通過set autocommit=0 禁止自動提交 
set autocommit=1 開啟自動提交來實現事務的處理。
但注意當你用 set autocommit=0 的時候,你以后所有的SQL都將做為事務處理,直到你用commit確認或rollback結束,并且只用于當前連接
※ MYSQL中只有INNODB和BDB類型的數據表才能支持事務處理!其他的類型是不支持!

3. 事務中一些有問題的讀取:臟讀,不可重復讀,幻讀

※臟讀:一個事務讀取了另一個未提交的并行事務寫的數據。
(事務T1更新了一行記錄的內容,但是并沒有提交所做的修改。事務T2讀取更新后的行,然后T1執行回滾操作,取消了剛才所做的修改。現在T2所讀取的行就無效了。)
 
exp:
小明的分數為89,事務A中把他的分數改為98,但事務A尚未提交。
與此同時,事務B正在讀取小明的分數,讀取到小明的分數為98。
隨后,事務A發生異常,而回滾了事務。小明的分數又回滾為89。
最后,事務B讀取到的小明的分數為98的數據即為臟數據,事務B做了一次臟讀。
 (大部分數據庫缺省的事物隔離級別都不會出現這種狀況)

※不可重復讀:一個事務重新讀取前面讀取過的數據,發現該數據已經被另一個已提交的事務修改過。
 (事務T1讀取一行記錄,緊接著事務T2修改了T1剛才讀取的那一行記錄。然后T1又再次讀取這行記錄,發現與剛才讀取的結果不同。這就稱為“不可重復”讀,因為T1原來讀取的那行記錄已經發生了變化。)
exp:
在事務A中,讀取到小明的分數為89,操作沒有完成,事務還沒提交。
與此同時,事務B把小明的分數改為98,并提交了事務。
隨后,在事務A中,再次讀取小明的分數,此時工資變為98。在一個事務中前后兩次讀取的結果并不致,導致了不可重復讀。

※幻讀:一個事務重新執行一個查詢,返回一套符合查詢條件的行,發現這些行因為其他最近提交的事務而發生了改變。
 (事務T1讀取一條指定的WHERE子句所返回的結果集。然后事務T2新插入 一行記錄,這行記錄恰好可以滿足T1所使用的查詢條件中的WHERE 子句的條件。然后T1又使用相同的查詢再次對表進行檢索,但是此時卻看到了事務T2剛才插入的新行。這個新行就稱為“幻像”,因為對T1來說這一行就像突 然出現的一樣。)
exp:
目前分數為90分以上的的學生有15人,事務A讀取所有分數為90分以上的的學生人數有15人。
此時,事務B插入一條分數為99的學生記錄。
這是,事務A再次讀取90分以上的的學生,記錄為16人。此時產生了幻讀。
(大部分數據庫缺省的事物隔離級別都會出現這種狀況,此種事物隔離級別將帶來表級鎖)

4. 事務隔離級別的描述

從級別低到高依次為:
READ UNCOMMITTED: 幻像讀、不可重復讀和臟讀都允許。
READ COMMITTED: 允許幻像讀、不可重復讀,但不允許臟讀。
REPEATABLE READ: 允許幻像讀,但不允許不可重復讀和臟讀。InnoDB默認級別
SERIALIZABLE: 幻像讀、不可重復讀和臟讀都不允許。

ORACLE默認的是 READ COMMITTED。
MYSQL默認的是 REPEATABLE READ。
但是InnoDB的可重復讀隔離級別和其他數據庫的可重復讀是有區別的,不會造成幻象讀(phantom read)。
ORACLE數據庫支持 READ COMMITTED 和 SERIALIZABLE ,不支持 READ UNCOMMITTED 和 REPEATABLE READ 。

如果數據庫的隔離級別為REAE_UNCOMMITTED, 則其他線程可以看到未提交的數據, 因此就出現臟讀;
如果數據庫隔離級別設為READ_COMMITTED,即沒提交的數據別人是看不見的,就避免了臟讀;但是,正在讀取的數據只獲得了讀取鎖,讀完之后就解鎖,不管當前事務有沒有結束,這樣就容許其他事務修改本事務正在讀取的數據。導致不可重復讀。
如果數據庫隔離級別設置為REPEATABLE READ,因為對正在操作的數據加鎖,并且只有等到事務結束才放開鎖, 則可以避免不可重復讀;
REPEATABLE READ只能保證正在被本事務操作的數據不被其他事務修改,卻無法保證有其他事務提交新的數據。 則有可能線程1在操作表T1的時候(特別是統計性的事務),其他線程仍然可以提交新數據到表T1,這樣會導致線程1兩次統計的結果不一致,就像發生幻覺一樣。
SERIALIZABLE因為獲得范圍鎖,且事務是一個接著一個串行執行,則保證了不會發生幻讀。
由此可見,隔離級別越高,受其他事物干擾越少,并發性能越差。

5. 修改事務隔離級別

在MySQL中默認事務隔離級別是可重復讀(Repeatable read).可通過SQL語句查詢;
查看InnoDB系統級別的事務隔離級別:SELECT @@global.tx_isolation;
查看InnoDB會話級別的事務隔離級別:SELECT @@tx_isolation;


修改事務隔離級別:
mysql> set global transaction isolation level read committed;
mysql> set session transaction isolation level read committed;

6. MySQL數據庫下測試結果


1)并發更新, 表tab1 的一條記錄id=1 num=1,兩個session分別執行事務1,2
分別輸入下面的語句

事務1 (session1) :

    start transaction;

    update tab1 set num=num+1 where id=1;

=========

一條記錄被更新,select發現num=2;

事務2 (session2):

    start transaction;

    update tab1 set num=num+1 where id=1;

=========

事務2會被阻塞,然后session1輸入commit;提交事務1。此時事務2更新成功。

session1 select一下會發現num還是2(不允許臟讀 );session2 select 一下 num 為3(update可以得到最新提交過的數據然后更新,但是如果沒有update,一直是select 的話select得到的num一直是1 ),然后commit;

session1 select 發現num為3 ,session2 select 發現num為3;

所以,事務中update是有行寫鎖(排他鎖)的,不會發生: 臟讀和不可重復讀(對于自身有update的事務,update之后是可以讀到最新數據的,這屬于例外?^_^ )

2)關于幻象讀 ,innodb默認事務隔離級別是不會出現的;摘自網上,測試通過:

mysql> set autocommit=off;
Query OK, 0 rows affected (0.00 sec)

session 1 創建表并插入測試數據

mysql> create table test(i int) engine=innodb;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test values(1);
Query OK, 1 row affected (0.00 sec)
session 2 查詢,沒有數據,正常,session1沒有提交,不允許臟讀

mysql> select * from test;
Empty set (0.00 sec)
session 1 提交事務

mysql> commit;
Query OK, 0 rows affected (0.00 sec)
session 2 查詢,還是沒有數據,沒有產生幻象讀

mysql> select * from test;
Empty set (0.00 sec)

3)最后,現在應該知道數據庫并發控制并不復雜,交給數據庫的事務就行,mysql 選用innodb引擎,不會出現“不可重復讀”。

在實際使用的過程中,會碰到多用戶并發操作數據庫導致數據錯誤的問題,解決方案下次再單獨寫篇博客記錄。

來自: http://my.oschina.net/liangbo/blog/364278

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!