一、事務四大特性(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