Mysql事務隔離級別以及不同隔離級別的並發事務問題


一、事務四大特性(ACID)

1、原子性(Atomicity)

     事務開始后所有操作,要么全部做完,要么全部不做。事務是一個不可分割的整體。事務在執行過程中出錯,會回滾到事務開始之前的狀態,以此來保證事務的完整性。

2、一致性(Consistency)

    事務在開始和結束后,能保證數據庫完整性約束的正確性即數據的完整性。轉賬為例,A向B轉賬,我們必須保證A扣了錢,B一定能收到錢。

3、隔離性(Isolation)

     事務之間的完全隔離,兩個事務互不影響。如A向一張銀行卡轉賬,避免在同一時間過多的操作導致賬戶金額的缺損,所以在A轉入結束之前是不允許其他事務對此卡操作。

4、持久性(Durability)

     事務的對數據的修改是永久性的。通俗的解釋為事務完成后,對數據的操作都要進行落盤(持久化)。事務一旦完成就是不可逆的,數據庫表現為事務一旦完成就無法回滾。

二、並發導致的事務問題

1、臟讀

      一個事務讀取另外一個事務還沒有提交的數據叫臟讀。

     例如:事務A讀取了事務B更新的數據,然后B回滾操作,那么A讀取到的數據是臟數據

2、不可重復讀

      同一個事務中,多次讀出的同一數據是不一致的。

      例如:事務 A 多次讀取同一數據,事務 B 在事務A多次讀取的過程中,對數據作了更新並提交,導致事務A多次讀取同一數據時,結果不一致。

3、幻讀

      同一個事務中,按照同一條件讀取出的數據量(也就是條數)不一致。

     例如:事務 A 多次讀取滿足條件(比如age>=10)的數據,事務 B 在事務A多次讀取的過程中,對數據作了新增或刪除,導致事務A多次讀取同一條件的數據時,數據量不一致。

     這兒講一下不可重復讀和幻讀的區別:

  • 不可重復讀:主要是說多次讀取一條記錄, 發現該記錄中某些列值被修改過。
  • 幻讀:主要是說多次讀取一個范圍內的記錄(包括直接查詢所有記錄結果或者做聚合統計), 發現結果不一致(標准檔案一般指記錄增多, 記錄的減少應該也算是幻讀)

     另外需要提一句,mysql的innodb的mvcc已經幫我們解決了幻讀的問題,后續我在給大家詳細解釋下mvcc。

三、不同隔離級別會產生的事物問題

 mysql默認隔離級別為:可重復讀(repeatable-read)

隔離級別 臟讀        不可重復度 幻讀     
讀未提交(read-uncommitted)
讀已提交(read-committed)
可重復讀(repeatable-read)
可串行化(serializable)

 

 

 

 

 

四、隔離級別詳細舉例

 

1、讀未提交(read-uncommitted)-- 臟讀

#首先,修改隔離級別
set tx_isolation='READ-UNCOMMITTED';
select @@tx_isolation;
+------------------+
| @@tx_isolation   |
+------------------+
| READ-UNCOMMITTED |
+------------------+

#事務A:啟動一個事務
start transaction;
select * from tx;
+------+------+
| id   | num  |
+------+------+
|    1 |    1 |
|    2 |    2 |
|    3 |    3 |
+------+------+

#事務B:也啟動一個事務(那么兩個事務交叉了)
       在事務B中執行更新語句,且不提交
start transaction;
update tx set num=10 where id=1;
select * from tx;
+------+------+
| id   | num  |
+------+------+
|    1 |   10 |
|    2 |    2 |
|    3 |    3 |
+------+------+

#事務A:那么這時候事務A能看到這個更新了的數據嗎?
select * from tx;
+------+------+
| id   | num  |
+------+------+
|    1 |   10 |   --->可以看到!說明我們讀到了事務B還沒有提交的數據
|    2 |    2 |
|    3 |    3 |
+------+------+

#事務B:事務B回滾,仍然未提交
rollback;
select * from tx;
+------+------+
| id   | num  |
+------+------+
|    1 |    1 |
|    2 |    2 |
|    3 |    3 |
+------+------+

#事務A:在事務A里面看到的也是B沒有提交的數據
select * from tx;
+------+------+
| id   | num  |
+------+------+
|    1 |    1 |      --->臟讀意味着我在這個事務中(A中),事務B雖然沒有提交,但它任何一條數據變化,我都可以看到!
|    2 |    2 |
|    3 |    3 |
+------+------+

 

2、讀已提交(read-committed)-- 不可重復讀

#首先修改隔離級別
set tx_isolation='read-committed';
select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+

#事務A:啟動一個事務
start transaction;
select * from tx;
+------+------+
| id   | num  |
+------+------+
|    1 |    1 |
|    2 |    2 |
|    3 |    3 |
+------+------+

#事務B:也啟動一個事務(那么兩個事務交叉了)
       在這事務中更新數據,且未提交
start transaction;
update tx set num=10 where id=1;
select * from tx;
+------+------+
| id   | num  |
+------+------+
|    1 |   10 |
|    2 |    2 |
|    3 |    3 |
+------+------+

#事務A:這個時候我們在事務A中能看到數據的變化嗎?
select * from tx;
+------+------+                
| id   | num  |                
+------+------+                
|    1 |    1 |--->並不能看到!  
|    2 |    2 |                
|    3 |    3 |                
+------+------+                
                               
#事務B:如果提交了事務B呢?         
commit;                        
                               
#事務A:                         
select * from tx;
+------+------+
| id   | num  |
+------+------+
|    1 |   10 |----------------->相同的select語句,兩次查詢結果不一樣
|    2 |    2 |
|    3 |    3 |
+------+------+

 

3、可重復讀repeatable-read)-- 幻讀

#首先,更改隔離級別
set tx_isolation='repeatable-read'; select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ #事務A:啟動一個事務 start transaction; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +------+------+ #事務B:開啟一個新事務(那么這兩個事務交叉了)
       在事務B中更新數據,並提交 start transaction; update tx set num=10 where id=1; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +------+------+ commit; #事務A:這時候即使事務B已經提交了,但A能不能看到數據變化? select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | --->還是看不到的!(這個級別2不一樣,也說明級別3解決了不可重復讀問題) | 2 | 2 | | 3 | 3 | +------+------+ #事務A:只有當事務A也提交了,它才能夠看到數據變化 commit; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +------+------+

 

4、可串行化(serializable)

 #首先修改隔離界別
set tx_isolation='serializable';
select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE   |
+----------------+

#事務A:開啟一個新事務
start transaction;

#事務B:在A沒有commit之前,這個交叉事務是不能更改數據的
start transaction;
insert tx values('4','4');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
update tx set num=10 where id=1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

 

參考文章

    https://www.cnblogs.com/snsdzjlz320/p/5761387.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM