大家有沒有想過一個問題,我們爲什麼需要事務?事務解決了怎樣的問題?事務又會帶來什麼新的問題?本人也是帶着疑惑一步步去深入學習,特此總結一下。
一、基礎概念
1.1 什麼是事務?
事務(Transaction) 是訪問和更新數據庫的程序執行單元,事務中可能包含一個或多個SQL語句。事務最大的特點就是,要麼SQL語句都執行,要麼都不執行。
1.2 事務條件
事務必須總是滿足以下的四個條件:
- 原子性(Atomicity)
- 一致性(Consistency)
- 隔離性(Isolation)
- 持久性(Durability)
以上四個條件簡稱ACID,下文將對事務條件逐一進行講解。
1.2.1 原子性
用一個最簡單的案例來說:
上面這個轉賬操作,可以分爲三條sql語句:
- 查詢張三餘額是否>100元,若滿足則執行後續SQL,否則返回。
- 張三餘額減去100元
- 李四餘額加100元
在這個案例當中,我們應該確保上面的轉賬操作要麼都成功進行,要麼都失敗,才能確保交易的合法性,即就是事務不會結束在書屋中間某個環節,如果在事務的執行過程中發生錯誤,則會回滾到事務開始的狀態。即滿足了事務的 原子性條件。
1.2.2 一致性
事物的一致性有點難理解,至少對於我是這樣的,而在我搜集很多資料總結的時候,我看到了這麼一句話:事務一致性是:應用系統從一個正確的狀態到另一個正確的狀態,我瞬間恍然大悟了。一致性關注數據的可見性,中間狀態的數據對外部不可見,只有最初狀態和最終狀態的數據對外可見。
1.2.3 隔離性
多個用戶訪問數據庫時,各自的事務具有隔離性,每個事務都有自己的數據空間,不會相互干擾。
1.2.4 持久性
執行事務提交後,數據會永久性保留在硬盤中。
二、事務隔離級別
mysql 8.0+可以查看當前隔離級別:
select @@transaction_isolation;
以前的老版本查看隔離級別是:
select @@tx_isolation;
mysql事務隔離級別分爲以下四種:
事務隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
讀未提交 (read-uncommitted) | 是 | 是 | 是 |
讀已提交 (read-committed) | 否 | 是 | 是 |
可重複讀 (repeatable-read) | 否 | 否 | 是 |
串行化 (serializable) | 否 | 否 | 否 |
名詞解釋:
髒讀:事務A和事務B同時執行,此時事務A還沒有提交修改的數據,但是事務B讀取到事務A改變後的數據,這就是髒讀。
不可重複讀:事務A讀取一條記錄,此時事務B對該數據 進行了操作(改變了原數據)並提交事務B,此時事務A再次查詢數據與第一次讀取的不一樣,這就是不可重複讀(兩次讀取的不一樣啊,就是說重複讀的話可能會前後矛盾,所以可以理解成不可重複讀。)。
幻讀:事務A、B同時開啓,若數據庫此時有兩條數據(id分別是1,2),事務B此時插入了一條數據(id爲3),同時提交事務B,此時事務A再插入id爲3的數據的時候,會提示插入失敗,但是查詢數據依然只能查到兩條數據,這就是幻讀。
不可重複讀重點在於update和delete,而幻讀的重點在於insert
在進行介紹之前,我們首先創建一個測試表。
CREATE DATABASE study;
use stydy;
CREATE TABLE person(
name varchar(20),
country varchar(20),
salary decimal,
id int (11) primary key auto_increment
);
insert into person(id,name,salary,country) values(1,"小王",1000,"中國");
2.1 讀未提交
讀未提交可能會出現髒讀、不可重複讀、幻讀。
(a)髒讀
髒讀:若同時有多個事務A,B開啓,事務B改變了數據但是沒有提交,但此時事務A中可以讀到事務B中未提交的數據。若此時B事務回滾,則出現了髒讀。
我們先來開啓兩個事務:
事務A | 事務B |
---|---|
set session transaction isolation level read uncommitted; | set session transaction isolation level read uncommitted; |
start transaction; | start transaction; |
select *from person; |
|
update person set salary=2000 where name=“小王”; | |
select *from person; | |
commit; | commit; |
可以看到,事務B未提交,但是已經影響了事務A,所以這就是髒讀。
2.2 讀已提交
讀已提交不會出現髒讀,但是還是會帶來不可重複讀和幻讀。
(a)不可重複讀:
若同時有多個事務A,B開啓,事務A先讀取一次數據,事務B改變了數據並提交事務B,此時事務A再讀取數據就是被事務B更新過的值,與第一次讀取的結果不一致,這就是不可重複讀。
我們先來開啓兩個事務:
事務A | 事務B |
---|---|
set session transaction isolation level read committed; | set session transaction isolation level read committed; |
start transaction; | start transaction; |
select *from person; |
|
update person set salary=2000 where name=“小王”; | |
select *from person; 可以看到已經解決了髒讀 |
|
commit; | |
select *from person; | |
commit; |
可以看到解決了髒讀,但是還是會出現讀取結果前後不一致的情況。即就是不可重複讀。
2.3 可重複讀
(a)幻讀
兩個事務同時開啓的時候,若一方對數據庫insert數據,則會出現幻讀!
我們先來開啓兩個事務:
事務A | 事務B |
---|---|
set session transaction isolation level repeatable-read; | set session transaction isolation level repeatable-read; |
start transaction; | start transaction; |
select *from person; |
|
update person set salary=2000 where name=“小王”; | |
select *from person; 可以看到已經解決了髒讀 |
|
commit; | |
select *from person; 可以看到已經解決了不可重複讀 |
|
start transaction; | |
insert person(id,name,salary,country) values(0,“小張”,5000,“加拿大”); | |
commit; | |
insert person(id,name,salary,country) values(0,“小張”,5000,“加拿大”); | |
select * from person; 出現了幻讀,明明只有一條數據還是插入失敗? |
|
commit; |
2.4 串行化隔離級別
事務隔離級別設置爲串行化時,如果同時開啓多個事務,爲保證其他事務不會幻讀到這個數據,insert數據時會“卡住”,直到其他事務提交。insert纔可以執行完成添加操作,這樣即就解決了幻讀問題!
總結:
以上例子說明了事務隔離級別不同可能會帶來不同的結果,另外事務隔離級別爲串行化時,讀寫數據都會鎖住整張表,隔離級別越高,越能保證數據的完整性和一致性,但是對併發性能的影響也越大。因爲這個並不常用。