五分鐘後,你將真正理解MySQL事務隔離級別!

什麼是事務?

事務是一組原子性的SQL操做,全部操做必須所有成功完成,若是其中有任何一個操做由於崩潰或其餘緣由沒法執行,那麼全部的操做都不會被執行。也就是說,事務內的操做,要麼所有執行成功,要麼所有執行失敗。mysql

事務的結束有兩種,當事務中的全部操做所有成功執行時,事務提交。若是其中一個操做失敗,將發生回滾操做,撤消以前到事務開始時的全部操做。sql

歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。數據庫

事務的特性

一個運行良好的事務處理系統,還須要具有四個特性:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持續性(Durability)。這四個特性簡稱爲ACID特性。安全

歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。微信

原子性(Atomicity)

一個事務必須被視爲一個不可分割的最小邏輯工做單元,整個事務中的全部操做要麼所有提交成功,要麼所有失敗回滾。對於一個事務來講,不可能只執行其中的一部分操做,而不執行其中的另一部分操做,這就是事務的原子性。session

歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。併發

一致性(Consistency)

事務執行的結果必須是從一個一致性的狀態轉換到另一個一致性的狀態。當數據庫只包含成功事務提交的結果時,就說數據庫處於一致性狀態。若是事務由於崩潰或其餘緣由還沒有完成,被迫中斷最終事務沒有提交,那麼事務中所作的修改也不會保存到數據庫中。高併發

歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。性能

隔離性(Isolation)

一般來講,一個事務的執行不能其它事務干擾。也就是說,一個事務內部的操做及使用的數據對其它併發事務是隔離的,併發執行的各個事務之間不能互相干擾。後面咱們將要講解隔離級別(Isolation Level)的時候,會發現爲何咱們要說「一般來講」是隔離的。atom

歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。

持續性(Durability)

事務一旦提交,它對數據庫中的數據的修改就應該是永久性的。此時即便系統崩潰,修改的數據也不會丟失。不過,實際上持久性也分不少不一樣的級別,有些持久性策略可以提供很是強的安全保障,而有些則未必。

歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。

事務隔離級別

在SQL標準中定義了四種隔離級別,每一種級別都定義了一個事務所作的修改,在另一個事務內和事務間,哪些是可見的,哪些是不可見的。低級別的隔離級通常支持更高的併發處理,並擁有更低的系統開銷。

歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。

未提交讀(Read Uncommitted)

未提交讀級別中,事務中的修改即便沒有提交,對其餘事務也是可見的。讀取到了事務沒有提交的數據,就被成爲髒讀(Dirty Read)。事務沒有提交的數據是很「髒」的,被讀取到會引發不少問題。從性能角度上看,未提交讀級別不會比其餘級別好不少,但缺少其餘級別的好處,因此在實際應用中不多被用到。

爲加上深對未提交讀級別的理解,讓咱們看一個髒讀的例子,首先設置事務隔離級別爲未提交讀

mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)

再檢驗一下事務隔離級別:

mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation   |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set, 1 warning (0.00 sec)

左右分別爲兩個用戶,左邊是用戶A,右邊是用戶B,時間線從上至下:

#用戶A:查詢user表,有一條OneMoreStudy的記錄      
mysql> select * from user;                       
+----+--------------+                            
| id | name         |                            
+----+--------------+                            
|  1 | OneMoreStudy |                            
+----+--------------+                            
1 row in set (0.00 sec)                          
                                                 
                                                             #用戶B:開始事務
                                                             mysql> start transaction;
                                                             Query OK, 0 rows affected (0.00 sec)
                                                             
                                                             #用戶B:更新user表的一條記錄
                                                             mysql> update user set name = 'OMS' where id = 1;
                                                             Query OK, 1 row affected (0.01 sec)
                                                             Rows matched: 1  Changed: 1  Warnings: 0
                                                 
#用戶A:查詢user表,有一條OMS的記錄,髒讀            
mysql> select * from user;                       
+----+------+                                    
| id | name |                                    
+----+------+                                    
|  1 | OMS  |                                    
+----+------+                                    
1 row in set (0.00 sec)                          
                                                 
                                                             #用戶B:提交事務
                                                             mysql> commit;
                                                             Query OK, 0 rows affected (0.00 sec)

歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。

提交讀(Read Committed)

提交讀級別中,一個事務開始時,只能查詢到其餘的事務已經提交的修改。也就是說,一個事務從開始到提交以前,任何的修改對其餘的事務都是不可見的。提交讀級別基本知足了事務的隔離性。

不過,在同一事務中兩次查詢之間,有其餘事務的修改被提交,那麼兩次查詢到結果可能不相同,這就是不可重複讀

爲了更好的理解不可重複讀,讓咱們看一個例子,首先設置事務隔離級別爲提交讀

mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)

再檢驗一下事務隔離級別:

mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
1 row in set, 1 warning (0.00 sec)

左右分別爲兩個用戶,左邊是用戶A,右邊是用戶B,時間線從上至下:

#用戶A:開始事務
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

#用戶A:查詢user表,有一條OneMoreStudy的記錄
mysql> select * from user;
+----+--------------+
| id | name         |
+----+--------------+
|  1 | OneMoreStudy |
+----+--------------+
1 row in set (0.00 sec)

                                                        #用戶B:更新user表的一條記錄
                                                        mysql> update user set name = 'OMS' where id = 1;
                                                        Query OK, 1 row affected (0.00 sec)
                                                        Rows matched: 1  Changed: 1  Warnings: 0

#用戶A:查詢user表,有一條OMS的記錄,不可重複讀
mysql> select * from user;
+----+------+
| id | name |
+----+------+
|  1 | OMS  |
+----+------+
1 row in set (0.00 sec)

#用戶A:提交事務
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。

可重複讀(Repeatable Read)

可重複讀級別中,保證了在同一個事務中屢次讀取一樣記錄的結果是一致的。即便屢次讀取之間有其餘事務對其結果作了修改,同一個事務中屢次讀取的結果也是一致的。可重複讀級別也是MySQL的默認事務隔離級別。

不過,當一個事務在讀過某個範圍內的記錄時,其餘事務又在這個範圍內插入了新的記錄,當以前的事務再一次讀取這個範圍的記錄時,不會讀取到新插入的那條記錄,這被稱爲幻讀

爲了更好的理解幻讀,讓咱們看一個例子,首先把事務隔離級別設置爲可重複讀

mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)

再檢驗一下事務隔離級別:

mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set, 1 warning (0.00 sec)

左右分別爲兩個用戶,左邊是用戶A,右邊是用戶B,時間線從上至下:

#用戶A:開始事務
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

#用戶A:查詢user表,有一條記錄
mysql> select * from user;
+----+--------------+
| id | name         |
+----+--------------+
|  1 | OneMoreStudy |
+----+--------------+
1 row in set (0.00 sec)

                                                            #用戶B:插入一條數據
                                                            mysql> insert into user (name) value ('OneMoreStudy');
                                                            Query OK, 1 row affected (0.01 sec)

#用戶A:查詢user表,仍是一條記錄,幻讀
mysql> select * from user;
+----+--------------+
| id | name         |
+----+--------------+
|  1 | OneMoreStudy |
+----+--------------+
1 row in set (0.00 sec)

#用戶A:提交事務
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

#用戶A:查詢user表,兩條記錄
mysql> select * from user;
+----+--------------+
| id | name         |
+----+--------------+
|  1 | OneMoreStudy |
|  2 | OneMoreStudy |
+----+--------------+
2 rows in set (0.00 sec)

歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。

可串行化(Serializable)

可串行化級別中,強制事務串行執行,是最高的隔離級別。在這個級別中,雖然避免了上面提到的幻讀,可是會在讀取的每一行上加鎖,可能致使大量的超時和鎖競爭問題,因此在實際應用中不多被用到。除非,很是須要確保數據一致性而且不要求高併發,能夠採用可串行化級別。

歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。

總結

本文首先簡單介紹了事務及其ACID特性,而後着重講解了事務的四種隔離級別:

  1. 未提交讀:事務中的修改即便沒有提交,對其餘事務也是可見的。
  2. 提交讀:事務開始時,只能查詢到其餘的事務已經提交的修改。
  3. 可重複讀:保證在同一個事務中屢次讀取一樣記錄的結果是一致的。
  4. 可串行化:強制事務串行執行。
隔離級別 髒讀 不可重複讀 幻讀
未提交讀 可能 可能 可能
提交讀 不可能 可能 可能
可重複讀 不可能 不可能 可能
可串行化 不可能 不可能 不可能

歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。

相關文章
相關標籤/搜索