文章目錄
- 事務
-
- 隐式事務
- 顯示事務
-
- 步驟
- ACID
-
- 原子性(Atomicity)
- 一緻性(Consistency)
- 隔離性(Isolation)
-
- 事務的并發問題
- 讀未送出
- 讀已送出
- 可重複讀
- 串行化
- 持久性(Durability)
- 總結
事務
定義:所謂事務,它是一個操作序列,這些操作要麼都執行,要麼都不執行,它是一個不可分割的工作機關。
準備工作:為了說明事務的ACID原理,我們使用銀行賬戶及資金管理的案例進行分析。
// 建立資料庫
create table account(
idint primary key not null,
namevarchar(40),
moneydouble
);
// 有兩個人開戶并存錢
insert into account values(1,'A',1000);
insert into account values(2,'B',1000);
隐式事務
沒有明顯的開啟和結束标記,比如dml語句的insert,update,delete語句本身就是一條事務。
insert into account values(1,‘A’,1000);
顯示事務
一般有多條sql語句組成,必須具有明顯的開啟和結束标記
步驟
-
取消隐式事務自動開啟的功能
show variables like “%autocommit%”; on/off
set autocommit = 0;
-
開啟事務
start transaction; //or begin;
-
編寫事務需要的sql語句(1條或者多條)
insert into account values(1,‘A’,1000);
insert into account values(2,‘B’,1000);
-
結束事務
送出
commit
復原
rollback;
ACID
ACID,是指在可靠資料庫管理系統(DBMS)中,事務(transaction)所應該具有的四個特性:原子性(Atomicity)、一緻性(Consistency)、隔離性(Isolation)、持久性(Durability).這是可靠資料庫所應具備的幾個特性.下面針對這幾個特性進行逐個講解.
原子性(Atomicity)
原子性是指事務是一個不可再分割的工作機關,事務中的操作要麼都發生,要麼都不發生。
- 案例
begin transaction
update account set money= money - 100where name='A';
update account set money= money +100where name='B';
if Error then
rollback
else
commit
-
分析
在事務中的扣款和加款兩條語句,要麼都執行,要麼就都不執行。否則如果隻執行了扣款語句,就送出了,此時如果突然斷電,A賬号已經發生了扣款,B賬号卻沒收到加款,在生活中就會引起糾紛。
-
解決方法
在資料庫管理系統(DBMS)中,預設情況下一條SQL就是一個單獨事務,事務是自動送出的。隻有顯式的使用start transaction開啟一個事務,才能将一個代碼塊放在事務中執行。保障事務的原子性是資料庫管理系統的責任,為此許多資料源采用日志機制。例如,SQL Server使用一個預寫事務日志,在将資料送出到實際資料頁面前,先寫在事務日志上。
一緻性(Consistency)
是指事務執行的結果必須是使資料庫從一個一緻性狀态變到另一個一緻性狀态。保證資料庫一緻性是指當事務完成時,必須使所有資料都具有一緻的狀态。在關系型資料庫中,所有的規則必須應用到事務的修改上,以便維護所有資料的完整性。
-
案例
對銀行轉帳事務,不管事務成功還是失敗,應該保證事務結束後ACCOUNT表中aaa和bbb的存款總額為2000元。
-
解決方法
保障事務的一緻性,可以從以下兩個層面入手
2.1 資料庫機制層面
資料庫層面的一緻性是,在一個事務執行之前和之後,資料會符合你設定的限制(唯一限制,外鍵限制,Check限制等)和觸發器設定。這一點是由SQL SERVER進行保證的。比如轉賬,則可以使用CHECK限制兩個賬戶之和等于2000來達到一緻性目的.
2.2 業務層面
對于業務層面來說,一緻性是保持業務的一緻性。這個業務一緻性需要由開發人員進行保證。當然,很多業務方面的一緻性,也可以通過轉移到資料庫機制層面進行保證。 比如使用者user1對表進行了更新操作,使用者user2在user1還沒有進行送出前讀表中資料,而且是大批量的讀取(打個比方:耗時3分鐘)而在這3分鐘内user1進行了送出操作,那又會産生什麼影響呢?這個時候怎麼保證讀寫一緻性呢?**這個時候DBMS就要保證有足夠大的臨時表來存放修改前的數值,**以保證user2讀取的資料是修改前的一緻資料.然後下次再讀取時候就是更新後的資料了.
隔離性(Isolation)
多個事務并發通路時,事務之間是隔離的,一個事務不應該影響其它事務運作效果。
這指的是在并發環境中,當不同的事務同時操縱相同的資料時,每個事務都有各自的完整資料空間。由并發事務所做的修改必須與任何其他并發事務所做的修改隔離。事務檢視資料更新時,資料所處的狀态要麼是另一事務修改它之前的狀态,要麼是另一事務修改它之後的狀态,事務不會檢視到中間狀态的資料。
在Windows中,如果多個程序對同一個檔案進行修改是不允許的,Windows通過這種方式來保證不同程序的隔離性:
企業開發中,事務最複雜問題都是由事務隔離性引起的。當多個事務并發時,MySql利用加鎖和阻塞來保證事務之間不同等級的隔離性。一般情況下,完全的隔離性是不現實的,完全的隔離性要求資料庫同一時間隻執行一條事務,這樣會嚴重影響性能。
事務的并發問題
1、髒讀:事務A讀取了事務B更新的資料,然後B復原操作,那麼A讀取到的資料是髒資料.
2、不可重複讀:事務 A 多次讀取同一資料,事務 B 在事務A多次讀取的過程中,對資料作了更新并送出,導緻事務A多次讀取同一資料時,結果 不一緻.
3、幻讀:系統管理者A将資料庫中所有學生的成績從具體分數改為ABCDE等級,但是系統管理者B就在這個時候插入了一條具體分數的記錄,當系統管理者A改結束後發現還有一條記錄沒有改過來,就好像發生了幻覺一樣,這就叫幻讀。
小結:不可重複讀的和幻讀很容易混淆,不可重複讀側重于修改,幻讀側重于新增或删除。解決不可重複讀的問題隻需鎖住滿足條件的行,解決幻讀需要鎖表
在MySQL中,實作了這四種隔離級别,分别有可能産生問題如下所示:
mysql預設的事務隔離級别為repeatable-read
讀未送出
(1)打開一個用戶端A,并設定目前事務模式為read uncommitted(未送出讀),查詢表account的初始值:
(2)在用戶端A的事務送出之前,打開另一個用戶端B,更新表account:
(3)這時,雖然用戶端B的事務還沒送出,但是用戶端A就可以查詢到B已經更新的資料:
(4)一旦用戶端B的事務因為某種原因復原,所有的操作都将會被撤銷,那用戶端A查詢到的資料其實就是髒資料:
(5)在用戶端A執行更新語句update account set balance = balance - 50 where id =1,lilei的balance沒有變成350,居然是400,是不是很奇怪,資料不一緻啊,如果你這麼想就太天真 了,在應用程式中,我們會用400-50=350,并不知道其他會話復原了,要想解決這個問題可以采用讀已送出的隔離級别
讀已送出
(1)打開一個用戶端A,并設定目前事務模式為read committed(未送出讀),查詢表account的所有記錄:
(2)在用戶端A的事務送出之前,打開另一個用戶端B,更新表account:
(3)這時,用戶端B的事務還沒送出,用戶端A不能查詢到B已經更新的資料,解決了髒讀問題:
(4)用戶端B的事務送出
(5)用戶端A執行與上一步相同的查詢,結果 與上一步不一緻,即産生了不可重複讀的問題
可重複讀
(1)打開一個用戶端A,并設定目前事務模式為repeatable read,查詢表account的所有記錄
(2)在用戶端A的事務送出之前,打開另一個用戶端B,更新表account并送出
(3)在用戶端A查詢表account的所有記錄,與步驟(1)查詢結果一緻,沒有出現不可重複讀的問題
(4)在用戶端A,接着執行update balance = balance - 50 where id = 1,balance沒有變成400-50=350,lilei的balance值用的是步驟(2)中的350來算的,是以是300,資料的一緻性倒是沒有被破壞。可重複讀的隔離級别下使用了MVCC機制,select操作不會更新版本号,是快照讀(曆史版本);insert、update和delete會更新版本号,是目前讀(目前版本)。
(5)重新打開用戶端B,插入一條新資料後送出
(6)在用戶端A查詢表account的所有記錄,沒有 查出 新增資料,是以沒有出現幻讀
串行化
(1)打開一個用戶端A,并設定目前事務模式為serializable,查詢表account的初始值.
(2)打開一個用戶端B,并設定目前事務模式為serializable,插入一條記錄報錯,表被鎖了插入失敗,mysql中事務隔離級别為serializable時會鎖表,是以不會出現幻讀的情況,這種隔離級别并發性極低,開發中很少會用到。
持久性(Durability)
持久性,意味着在事務完成以後,該事務所對資料庫所作的更改便持久的儲存在資料庫之中,并不會被復原。
即使出現了任何事故比如斷電等,事務一旦送出,則持久化儲存在資料庫中。
總結
- 事務隔離級别為讀送出時,寫資料隻會鎖住相應的行。
- 事務隔離級别為可重複讀時,如果檢索條件有索引(包括主鍵索引)的時候,預設加鎖方式是next-key 鎖;如果檢索條件沒有索引,更新資料時會鎖住整張表。一個間隙被事務加了鎖,其他事務是不能在這個間隙插入記錄的,這樣可以防止幻讀。
- .事務隔離級别為串行化時,讀寫資料都會鎖住整張表。
- 隔離級别越高,越能保證資料的完整性和一緻性,但是對并發性能的影響也越大。
參考:
https://www.cnblogs.com/wyaokai/p/10921323.html (MySQL的四種事務隔離級别)