前言
事務(Transaction)是數據庫學習中非常重要的一種概念,作為關系型數據庫的核心組成,在數據安全方面有着非常重要的作用。
事務在各大數據庫中都有非常廣泛的應用,並且對於很多業務,例如電商、支付,是保證其可以正常運行的根基。
本文會逐步解析數據庫事務的核心特性,以獲得對事務更深的理解,主要以MySql的InnoDB引擎來講解。
希望對各位有所幫助,覺得不錯可以給點個贊哦~
事務的定義
事務是一個數據庫操作序列,由事務開始與事務結束之間執行的全部數據庫操作組成,這些操作要么全部執行,要么全部不執行,是一個不可分割的工作單位。
白話講就是,要么做完,要么別做,做一半了別想跑,你得給把爛攤子收好再走人!
事務中的四大特性:ACID
小案例
什么是ACID,即:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)
來看一個銀行轉賬的案例簡單體會一下,然后我們再來詳細解釋
小明用網銀給小紅轉賬100元,在這種交易的過程中,有幾個問題需要思考
- A:如何同時保證,小明總金額減少100元,小紅總金額增加100元?
- C:小明正在轉賬時,小李用小明的卡在ATM提走了全部的錢,小明還能轉賬成功嗎?
- I:小明正在轉賬時,小李用小明的卡查詢小明的余額,小明的錢少沒少?
- D:如果數據庫突然崩潰,保證交易數據會不會成功保存在數據庫中?
ACID
原子性
一個事務中的所有操作,要么全部完成,要么全部不完成,不會結束在中間某個環節。
事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。
一致性
事務的一致性指的是在一個事務執行之前和執行之后數據庫都必須處於一致性狀態。
如果事務成功地完成,那么系統中所有變化將正確地應用,系統處於有效狀態。
如果在事務中出現錯誤,那么系統中的所有變化將自動地回滾,系統返回到原始狀態。
一致性依賴於原子性和隔離性。
隔離性
指的是在並發環境中,當不同的事務同時操縱相同的數據時,每個事務都有各自的完整數據空間。
由並發事務所做的修改必須與任何其他並發事務所做的修改隔離。
事務查看數據更新時,數據所處的狀態要么是另一事務修改它之前的狀態,要么是另一事務修改它之后的狀態,事務不會查看到中間狀態的數據。
持久性
指的是只要事務成功結束,它對數據庫所做的更新就必須永久保存下來。
即使發生系統崩潰,重新啟動數據庫系統后,數據庫還能恢復到事務成功結束時的狀態。
回到案例
- 原子性確保了不管交易過程中發生了什么意外狀況,小明和小紅的金額變動要么同時成功,要么同時失敗
- 一致性確保錢不會在系統內憑空產生或消失, 所以小明的轉賬是會被退回的
- 隔離性保證了在事務提交之前,其他事務是無法得知其內部變化的,所以小李檢查時錢不會少
- 持久性確保如果事務剛剛提交,數據庫就發生崩潰,執行的結果依然會保存在數據庫中
ACID根本問題,是針對不同的事務同時對同一份數據進行寫操作的。
實現原理
InnoDB是MySql中唯一支持事務的存儲引擎,那么它是如何實現ACID的呢?
主要通過兩門技術:並發控制技術和日志恢復技術
並發控制技術保證了事務的隔離性,使數據庫的一致性不會因為並發執行的操作被破壞。
日志恢復技術保證了事務的原子性,使一致性、持久性不會因事務或系統故障被破壞。

並發控制
主要就是上鎖,流程如下:
- 先獲得了鎖,然后才能修改對應的數據A
- 事務完成后釋放鎖,給下一個要修改數據A的事務
- 同一時間,只能有一個事務持有數據A的互斥鎖
- 沒有獲取到鎖的事務,需要等待鎖釋放
日志恢復
- Redo Log:
Redo Log記錄的是新數據的備份。在事務提交前,只要將Redo Log持久化即可,不需要將數據持久化。當系統崩潰時,雖然數據沒有持久化,但是Redo Log已經持久化。系統可以根據Redo Log的內容,將所有數據恢復到最新的狀態。
- Undo Log:
Undo Log是舊數據的備份,在操作任何數據之前,首先將數據備份到Undo Log,然后進行數據的修改。如果出現了錯誤或者用戶執行了回滾語句,系統可以利用Undo Log中的備份將數據恢復到事務開始之前的狀態。
事務的隔離級別
前面提到了,數據庫的隔離性是依靠並發控制來實現的,也就是通過加鎖來實現的。
數據庫是個高並發的應用,同一時間會有大量的並發訪問,如果加鎖過度,會極大的降低並發處理能力。
所以對於加鎖的處理,就成了數據庫的重中之重。
一次封鎖鎖/兩段鎖
一次封鎖法,就是在方法的開始階段,已經預先知道會用到哪些數據,然后全部鎖住,在方法運行之后,再全部解鎖。
這種方式是最為熟知和常用的方法,可以有效的避免循環死鎖。
一次封鎖法在數據庫中並不好用,因為在事務開始階段,數據庫並不知道會用到哪些數據。
數據庫采用的是兩段鎖,將事務分成兩個階段:
- 加鎖階段:
- 在對任何數據進行讀操作之前要申請並獲得S鎖,即共享鎖,其它事務可以繼續加共享鎖,但不能加排它鎖。
- 在進行寫操作之前要申請並獲得X鎖,即排它鎖,其它事務不能再獲得任何鎖。
- 加鎖不成功,則事務進入等待狀態,直到加鎖成功才繼續執行。
- 解鎖階段:當事務釋放了一個封鎖以后,事務進入解鎖階段,在該階段只能進行解鎖操作不能再進行加鎖操作。
事務 | 加鎖/解鎖處理 |
---|---|
begin; | |
insert into test ….. | 加insert對應的鎖 |
update test set… | 加update對應的鎖 |
delete from test …. | 加delete對應的鎖 |
commit; | 事務提交時,同時釋放insert、update、delete對應的鎖 |
兩段鎖無法避免死鎖,但是兩段鎖協議可以保證事務的並發調度是串行化,關於穿行化后面會進行解釋。
四種隔離級別
隱式事務,即單條語句。
前置概念
了解隔離級別之前,我們需要先了解三個概念,臟讀、幻讀、不可重復讀。
臟讀
所謂臟讀是指一個事務中訪問到了另外一個事務未提交的數據,如圖:

不可重復讀
一個事務讀取同一條記錄2次,得到的結果不一致,如圖:

幻讀
一個事務讀取2次,得到的記錄條數不一致,如圖:

隔離級別
在數據庫操作中,為了有效保證並發讀取數據的正確性,提出的事務隔離級別。
我們的數據庫鎖,也是為了構建這些隔離級別存在的。
隔離級別 | 臟讀(Dirty Read) | 不可重復讀(NonRepeatable Read) | 幻讀(Phantom Read) |
---|---|---|---|
未提交讀(Read uncommitted) | ✅ | ✅ | ✅ |
已提交讀(Read committed) | ❌ | ✅ | ✅ |
可重復讀(Repeatable read) | ❌ | ❌ | ✅ |
可串行化(Serializable ) | ❌ | ❌ | ❌ |
可重復讀是InnoDB默認級別
總結
-
事務的 ACID 四大基本特性在很大程度上保證了數據庫的安全運行。
-
雖然ACID有很多好處,但是也有一些缺點,尤其是隔離性會對性能有比較大影響,所以在實際的使用中我們也會根據業務的需求對隔離性進行調整。