MySQL系列
文章目錄
- MySQL系列
- 一、什么是事務
- 1.1事務的核心概念
- 1.2、 事務的四大屬性(ACID)
- 1.2.1 原子性(Atomicity)
- 1.2.2 一致性(Consistency)
- 1.2.3 隔離性(Isolation)
- 1.2.4 持久性(Durability)
- 1.3 為什么會出現事務
- 1.5 事務的版本支持
- 二、事務操作
- 2.1 事務的提交
- 2.2 事務的基本操作
- 2.3 單挑sql和事務的關系
- 三、事務隔離級別
- 3.1 隔離級別
- 3.2 隔離級別的操作
- 3.2.1查看隔離級別
- 3.2.2 設置隔級別
- 四、隔離級別的具體表現
- 4.1 讀未提交
- 4.2 讀提交
- 4.3 可重復讀
- 4.4 串行化
- 4.5 總結
上一篇:MySQL 索引:結構、對比與操作實踐指南
一、什么是事務
1.1事務的核心概念
事務是數據庫操作中的核心概念,其核心定義可概括為:
由一組DML語句(數據操縱語言,如INSERT、UPDATE、DELETE等)組成,這些語句在邏輯上存在緊密相關性(例如完成一次轉賬需同時涉及轉出賬戶扣款和轉入賬戶收款)。
作為一個不可分割的整體,這組DML語句遵循要么全部成功執行,要么全部失敗的原則——只要其中任一語句執行出錯,所有已執行的操作都會被撤銷。
此外,事務確保:不同客戶端在操作過程中,看到的數據狀態可能是不相同的,以此避免并發操作導致的數據混亂。
一個事務不僅僅只是簡單的sql集合,同時滿足如下四個屬性
1.2、 事務的四大屬性(ACID)
這里先簡單了解,后面的內容,會圍繞這四大特定詳細介紹
MySQL做為網絡服務器一定會存在并發訪問的場景,而這就可能發生圖中錯誤,要想解決這個問題,就需要保證:
1.買票的過程得是原子
2. 買票互相不能影響
3. 買完票要永久有效
4. 買前,和買后都要是確定的狀態
為了解決這個問題,MySQL對事務做了如下要求:
1.2.1 原子性(Atomicity)
事務(transaction)里的所有操作是一個不可分割的整體,要么全部成功執行,要么全部回滾撤銷 。若執行中出現錯誤,會回滾(Rollback)到事務開始前狀態,如同事務從未執行過,保障操作的“一榮俱榮,一損俱損”。
1.2.2 一致性(Consistency)
事務執行前和執行后,數據庫的完整性(如數據精度、關聯性等規則)不會被破壞 。寫入的數據必須嚴格符合預設規則,確保數據庫能按預期提供服務,維持數據的安全可靠。
1.2.3 隔離性(Isolation)
數據庫支持多個事務并發讀寫、修改數據,且能通過隔離性避免因事務交叉執行導致數據混亂 。隔離性有不同級別,常見的有:讀未提交(Read Uncommitted )、讀提交(Read Committed )、可重復讀(Repeatable Read )、串行化(Serializable ),不同級別平衡并發效率與數據一致性。
1.2.4 持久性(Durability)
事務成功提交后,對數據的修改會永久留存 ,即便系統故障(如斷電、崩潰),修改也不會丟失,保障數據最終的“穩定落地”。
這四大屬性的英文首字母縮寫為 ACID ,是事務機制的核心保障。
1.3 為什么會出現事務
MySQL中事務的設計,本質是為應用程序訪問數據庫時提供便利——它能極大簡化編程模型,讓開發者無需手動處理各種潛在錯誤與并發問題。
試想,若沒有事務機制,編寫數據庫訪問程序時,需額外考慮的問題會極為繁瑣:比如網絡突然中斷如何處理?服務器意外宕機后數據如何恢復?多個操作同時修改同一份數據時如何避免沖突?…… 而有了事務,開發者只需關注“提交(Commit)”或“回滾(Rollback)”兩種操作即可,上述復雜場景都由事務機制自動處理。
由此可見,事務并非數據庫系統與生俱來的組件,而是專為服務應用層而設計的工具,其核心價值在于降低應用程序與數據庫交互時的開發復雜度。
1.5 事務的版本支持
show engines;
可以看到并不是所有的引擎都支持事務。
二、事務操作
2.1 事務的提交
事務的提交方式常見的有兩種:
- 自動提交
- 手動提交
查看是否的提交方式:
show variables like 'autocommit';
可以看到默認情況下是打開的;
更改自動提交:
// 禁止自動提交:
SET AUTOCOMMIT=0;
// 開啟自動提交:
SET AUTOCOMMIT=1;
兩者的具體區別后面會做驗證
2.2 事務的基本操作
接下來我會營造并發場景展開介紹
準備工作:
為了便于演示,先將mysql的默認隔離級別設置成讀未提交
set global transaction isolation level READ UNCOMMITTED;
重啟終端(也可以重新登錄MySQL),進行查看隔離級別
select @@transaction_isolation;
創建測試表
create table if not exists account(
id int primary key,
name varchar(50) not null default '',
blance decimal(10,2) not null default 0.0
)ENGINE=InnoDB DEFAULT CHARSET=UTF8;
啟動事務(兩種方法):
- start transaction;
- begin
設置保存點(標記,方便回滾) - savepoint 保存點名
可以看到我們在read uncommitted
級別的隔離下,并發操作一整表時,事務發起者向表中插入后不需要等到事務提交,并發參與者立馬可以看到,事務發起者在執行操作時也可以邊操作邊插入保存點,可以通過rollback to 保存點名
回滾到設置保存點前的狀態(若為設置保存點,可以直接使用rollback
直接回滾到事務開始狀態),此時若我們的操作失敗、服務器奔潰,就會自動回滾到,執行事務前的狀態。
可以看到事務發起者異常退出時,事務就會回滾到開始前狀態,數據不會被持久化,采用這種事務的形式,就實現了原子化的概念。
有很多場景在這里不可能全部演示,你可以自己嘗試,使用ctrl+\可以模擬服務退出
commit
提交事務
當我們提交事務后,數據就會永久保存,再次回滾就不會回到之前了
只要事務發起者commit
后數據機會持久化,即使再次回滾數據依然保存。
我們不是開啟自動提交了嗎?我什么異常退出后,事務無法自動提交呢?
MySQL要求,手動發起的事務,必須要手動提交,而自動提交是幫助默認發起的事務做提交的,下面我們來驗證一下。
2.3 單挑sql和事務的關系
關閉自動提交 set autocommit=0
可以看到,當關閉自動提交后,事務發起者向表中插入數據時,若MySQL服務異常退出,數據會回滾到之前狀態。這是因為在MySQL中,單條SQL語句默認也會被當作事務處理,其提交依賴自動提交功能。當關閉自動提交后,即便執行單條SQL(本質也是事務范疇),因未手動提交,異常退出時就無法完成提交,最終觸發回滾 。
相反的,在正常情況下,自動提交方法是打開的,那么即使mysql崩潰了,它數據也是會自動提交成功的(所以執行的所有sql本質都是事務!!只不過進行了自動提交)。
總結
- 只要輸入 begin 或者 start transaction,事務便必須要通過 commit 提交,才會持久化,與是否設置 set autocommit 無關。
- 事務可以 手動回滾,同時,當操作異常,MySQL 會 自動回滾。
- 對于 InnoDB,每一條 SQL 語言都默認封裝成事務,自動提交(select 有特殊情況,因為 MySQL 有 MVCC)。
- 從上面的例子,我們能看到事務本身的 原子性(回滾),持久性(commit)。
- 如果沒有設置 保存點,也可以回滾,只能回滾到事務的開始,直接使用 rollback(前提是事務還沒有提交)。
- 如果一個事務被 提交了(commit),則 不可以回退(rollback)。
- 可以選擇回退到 哪個保存點。
- InnoDB 支持事務,MyISAM 不支持事務。
- 開始事務可以使用 start transaction 或者 begin。
三、事務隔離級別
首先需要明確:MySQL服務作為網絡服務器,可能同時被多個客戶端進程(線程)訪問,且訪問以事務形式進行。一個事務可能包含多條SQL,因此任何事務都存在三個階段:
- 執行前
- 執行中
- 執行后
而原子性的核心,是讓用戶層要么看到事務執行前的狀態,要么看到執行后的狀態。若執行中出現問題,可隨時通過回滾撤銷操作。因此,單個事務對用戶呈現的核心特性就是原子性。
但需注意:所有事務都有執行過程,當多個事務并發執行多條SQL時,仍可能出現互相影響——例如,多個事務同時訪問同一張表甚至同一行數據。
而有些情況不會對操作造成影響(如:并發讀取數據),有些情況則會導致結果出錯(如:一端刪除、一端查找可能引發執行錯誤)。
- 因此,數據庫中為保證事務執行過程盡量不受干擾,存在一個重要特征:隔離性;
- 同時,數據庫允許事務受不同程度的干擾,由此衍生出另一種重要特征:隔離級別。
3.1 隔離級別
讀未提交(Read Uncommitted)
- 核心特征:所有事務可看到其他事務未提交的執行結果。
- 問題:相當于無隔離性,會引發臟讀、幻讀、不可重復讀等所有并發問題。 (上面的測試就是使用的這個隔離級別)
讀提交(Read Committed)
- 核心特征:事務僅能看到其他事務已提交的修改(滿足隔離性的基礎定義)。
- 問題:會導致不可重復讀(同一事務中多次
select
可能得到不同結果)。 - 默認情況:是大多數數據庫的默認隔離級別(非 MySQL 默認)。
可重復讀(Repeatable Read)
- 核心特征:確保同一事務中,多次讀取數據時看到的數據行一致。
- 問題:仍可能存在幻讀。
- 默認情況:MySQL 的默認隔離級別。
串行化(Serializable)
- 核心特征:事務的最高隔離級別,通過強制事務排序避免沖突,解決幻讀問題(原理是對讀取的數據行加共享鎖)。
- 問題:可能導致超時和鎖競爭,性能極低。
- 使用場景:實際生產中基本不使用(隔離級別過于嚴格)。
3.2 隔離級別的操作
3.2.1查看隔離級別
查看全局隔離級別
select @@global.transaction_isolation;
查看當前會話隔離級別
select @@session.transaction_isolation;
select @@transaction_isolation;
3.2.2 設置隔級別
設置當前會話 or 全局隔離級別語法:
set [ session | global ] transaction isolation level { read uncommitted | read committed | repeatable read | serializable}
設置當前會話的隔離級別
set session transaction isolation level read committed;
設置全局的隔離級別
set global transaction isolation level serializable;
可以看到全局隔離級別的改變,不會對以創建的會話產生作用,而他的作用是設置新建的會話。
也就是說重啟mysql后就會變成全局的隔離級別
四、隔離級別的具體表現
4.1 讀未提交
讀未提交:幾乎不施加鎖機制,雖執行效率高,但存在嚴重問題
其核心問題是臟讀(dirty read):一個事務在執行過程中,能讀到另一個未提交事務的更新(或其他操作)—— 因為事務執行有過程性,只要一個用戶修改了表,即便未執行 commit
,其他用戶無需等待提交就能立即看到這些未確認的操作。
這正是“讀未提交”的本質:事務只要執行了SQL(未提交),其對表的操作就會被其他事務可見。
臟讀具體來說:
- 事務A(發起者) 對數據做了修改(如更新某條記錄),但尚未執行
commit
(可能處于執行中或未完成); - 事務B(并發者) 在自己的事務中讀取到了事務A未提交的修改結果;
- 若事務A因異常(如代碼錯誤、數據庫崩潰等)發生回滾,其修改會被撤銷,數據恢復到初始狀態;
- 此時事務B之前讀取到的“未提交數據”就成了無效的錯誤數據,基于該數據的后續操作(如計算、決策、更新)也會隨之出錯。
這種“讀到臨時且可能被回滾的數據”的現象,正是臟讀。
4.2 讀提交
為方便截取我先把表清空
在讀提交隔離級別中:
- 事務發起者未提交時,事務并發者看不到其操作;
- 一旦發起者執行 commit 提交,即使并發者的事務未結束,也會立即看到提交后的修改。
這會導致 不可重復讀(non-repeatable read) 問題:同一事務內,相同的讀取操作在不同時間點(事務仍在執行中)返回不同結果。
從邏輯上,一個事務提交后其他事務能看到最新數據是合理的,但站在并發事務的一致性角度,同一事務內多次讀取應保持一致,否則會破壞事務內數據的穩定性——這正是不可重復讀的問題核心。
示例:
- 事務A(并發者)啟動,讀取到
account
表中id=1
的余額為1000
元。 - 事務B(發起者)啟動,將
id=1
的余額改為2000
元并提交。 - 事務A未結束,再次讀取
id=1
的余額,結果變為2000
元——同一事務內兩次讀取結果不同,即不可重復讀。
4.3 可重復讀
可重復度是MySQL的默認隔離級別
可重復讀(MySQL 默認隔離級別)的核心特性是:同一事務在執行期間,多次讀取數據會保持一致,直到自身事務提交后,才能看到其他事務的修改結果。
例如:終端A在事務中執行
INSERT
操作,終端B在自身事務周期內多次查看,均不受終端A未提交或已提交數據的影響,這符合可重復讀的特點。
但多數數據庫的可重復讀存在一個問題:無法屏蔽其他事務新插入(
INSERT
)的數據。
原因在于:隔離性通常通過對已存在數據加鎖實現,而待插入的數據因尚未存在,無法被傳統鎖機制覆蓋。因此,同一事務內多次查詢時,可能會讀到其他事務新插入的記錄,出現“多次查找結果不一致、新增了未預期的記錄”的現象,這被稱為 幻讀(phantom read)。
關鍵差異:MySQL 的可重復讀(RR 級別)通過 Next-Key 鎖(間隙鎖 + 行鎖) 解決了幻讀問題——不僅鎖定已有數據行,還鎖定數據間隙,防止其他事務在間隙中插入新記錄,從而保證事務內讀取結果的穩定性。
4.4 串行化
前面三個隔離級別主要特點是:一方在修改數據(CUD操作),一方在讀取數據(R操作),但是面對雙方都在修改數據的場景(CUD操作),我們就必須通過加鎖來實現了
對所有事務操作全部加鎖,進行事務的串行化,但是只要串行化,效率很低,幾乎完全不會被采用。
可以看到在串行化隔離級別下,并發的事務查詢并不會受到鎖的約束,但如果想要對數據做修改,sql就會被阻塞(阻塞時間過長就會報錯)。
當有一方提交事務后,鎖就會被釋放,阻塞的sql就會再次執行。
4.5 總結
隔離級別越嚴格,數據安全性越高,但數據庫并發性能越低,實際應用中需在兩者間尋找平衡。
- 不可重復讀的重點是修改和刪除:同一條件下,兩次讀取同一數據,結果值不同。
- 幻讀的重點是新增:同一條件下,兩次讀取的記錄數量不同。
從實例中可看出,事務存在長事務、短事務的概念。事務間的互相影響,在并行執行且均未提交時尤為顯著。
一致性(Consistency)
-
核心定義:事務執行結果必須使數據庫從一個一致性狀態切換到另一個一致性狀態。當數據庫僅包含所有成功提交事務的結果時,處于一致性狀態;若因系統中斷導致未完成事務的修改寫入數據庫,則數據庫會處于不一致狀態。
-
保障關系:一致性通過原子性(事務要么全成、要么全滾)提供基礎保障。
-
技術支撐:從技術層面,一致性(C)通過原子性(A)、隔離性(I)、持久性(D) 共同保障(即 ACID 中的 AID 支撐 C)。
后面這篇文章是對隔離性更深層次的介紹