Mysql數(shù)據(jù)庫事務(wù)概念、操作與隔離級別全解析
MySQL系列
上一篇:MySQL 索引:結(jié)構(gòu)、對比與操作實(shí)踐指南
一、什么是事務(wù)
1.1事務(wù)的核心概念
事務(wù)是數(shù)據(jù)庫操作中的核心概念,其核心定義可概括為:
由一組DML語句(數(shù)據(jù)操縱語言,如INSERT、UPDATE、DELETE等)組成,這些語句在邏輯上存在緊密相關(guān)性(例如完成一次轉(zhuǎn)賬需同時(shí)涉及轉(zhuǎn)出賬戶扣款和轉(zhuǎn)入賬戶收款)。
作為一個(gè)不可分割的整體,這組DML語句遵循要么全部成功執(zhí)行,要么全部失敗的原則——只要其中任一語句執(zhí)行出錯(cuò),所有已執(zhí)行的操作都會(huì)被撤銷。
此外,事務(wù)確保:不同客戶端在操作過程中,看到的數(shù)據(jù)狀態(tài)可能是不相同的,以此避免并發(fā)操作導(dǎo)致的數(shù)據(jù)混亂。
一個(gè)事務(wù)不僅僅只是簡單的sql集合,同時(shí)滿足如下四個(gè)屬性
1.2、 事務(wù)的四大屬性(ACID)
這里先簡單了解,后面的內(nèi)容,會(huì)圍繞這四大特定詳細(xì)介紹

MySQL做為網(wǎng)絡(luò)服務(wù)器一定會(huì)存在并發(fā)訪問的場景,而這就可能發(fā)生圖中錯(cuò)誤,要想解決這個(gè)問題,就需要保證:
1.買票的過程得是原子
2. 買票互相不能影響
3. 買完票要永久有效
4. 買前,和買后都要是確定的狀態(tài)
為了解決這個(gè)問題,MySQL對事務(wù)做了如下要求:
1.2.1 原子性(Atomicity)
事務(wù)(transaction)里的所有操作是一個(gè)不可分割的整體,要么全部成功執(zhí)行,要么全部回滾撤銷 。若執(zhí)行中出現(xiàn)錯(cuò)誤,會(huì)回滾(Rollback)到事務(wù)開始前狀態(tài),如同事務(wù)從未執(zhí)行過,保障操作的“一榮俱榮,一損俱損”。
1.2.2 一致性(Consistency)
事務(wù)執(zhí)行前和執(zhí)行后,數(shù)據(jù)庫的完整性(如數(shù)據(jù)精度、關(guān)聯(lián)性等規(guī)則)不會(huì)被破壞 。寫入的數(shù)據(jù)必須嚴(yán)格符合預(yù)設(shè)規(guī)則,確保數(shù)據(jù)庫能按預(yù)期提供服務(wù),維持?jǐn)?shù)據(jù)的安全可靠。
1.2.3 隔離性(Isolation)
數(shù)據(jù)庫支持多個(gè)事務(wù)并發(fā)讀寫、修改數(shù)據(jù),且能通過隔離性避免因事務(wù)交叉執(zhí)行導(dǎo)致數(shù)據(jù)混亂 。隔離性有不同級別,常見的有:讀未提交(Read Uncommitted )、讀提交(Read Committed )、可重復(fù)讀(Repeatable Read )、串行化(Serializable ),不同級別平衡并發(fā)效率與數(shù)據(jù)一致性。
1.2.4 持久性(Durability)
事務(wù)成功提交后,對數(shù)據(jù)的修改會(huì)永久留存 ,即便系統(tǒng)故障(如斷電、崩潰),修改也不會(huì)丟失,保障數(shù)據(jù)最終的“穩(wěn)定落地”。
這四大屬性的英文首字母縮寫為 ACID ,是事務(wù)機(jī)制的核心保障。
1.3 為什么會(huì)出現(xiàn)事務(wù)
MySQL中事務(wù)的設(shè)計(jì),本質(zhì)是為應(yīng)用程序訪問數(shù)據(jù)庫時(shí)提供便利——它能極大簡化編程模型,讓開發(fā)者無需手動(dòng)處理各種潛在錯(cuò)誤與并發(fā)問題。
試想,若沒有事務(wù)機(jī)制,編寫數(shù)據(jù)庫訪問程序時(shí),需額外考慮的問題會(huì)極為繁瑣:比如網(wǎng)絡(luò)突然中斷如何處理?服務(wù)器意外宕機(jī)后數(shù)據(jù)如何恢復(fù)?多個(gè)操作同時(shí)修改同一份數(shù)據(jù)時(shí)如何避免沖突?…… 而有了事務(wù),開發(fā)者只需關(guān)注“提交(Commit)”或“回滾(Rollback)”兩種操作即可,上述復(fù)雜場景都由事務(wù)機(jī)制自動(dòng)處理。
由此可見,事務(wù)并非數(shù)據(jù)庫系統(tǒng)與生俱來的組件,而是專為服務(wù)應(yīng)用層而設(shè)計(jì)的工具,其核心價(jià)值在于降低應(yīng)用程序與數(shù)據(jù)庫交互時(shí)的開發(fā)復(fù)雜度。
1.4 事務(wù)的版本支持
show engines;

可以看到并不是所有的引擎都支持事務(wù)。
二、事務(wù)操作
2.1 事務(wù)的提交
事務(wù)的提交方式常見的有兩種:
- 自動(dòng)提交
- 手動(dòng)提交
查看是否的提交方式:
show variables like 'autocommit';

可以看到默認(rèn)情況下是打開的;
更改自動(dòng)提交:
// 禁止自動(dòng)提交: SET AUTOCOMMIT=0;

// 開啟自動(dòng)提交: SET AUTOCOMMIT=1;

兩者的具體區(qū)別后面會(huì)做驗(yàn)證
2.2 事務(wù)的基本操作
接下來我會(huì)營造并發(fā)場景展開介紹
準(zhǔn)備工作:
為了便于演示,先將mysql的默認(rèn)隔離級別設(shè)置成讀未提交
set global transaction isolation level READ UNCOMMITTED;
重啟終端(也可以重新登錄MySQL),進(jìn)行查看隔離級別
select @@transaction_isolation;

創(chuàng)建測試表
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;
啟動(dòng)事務(wù)(兩種方法):
- start transaction;
- begin
設(shè)置保存點(diǎn)(標(biāo)記,方便回滾) - savepoint 保存點(diǎn)名

可以看到我們在read uncommitted級別的隔離下,并發(fā)操作一整表時(shí),事務(wù)發(fā)起者向表中插入后不需要等到事務(wù)提交,并發(fā)參與者立馬可以看到,事務(wù)發(fā)起者在執(zhí)行操作時(shí)也可以邊操作邊插入保存點(diǎn),可以通過rollback to 保存點(diǎn)名回滾到設(shè)置保存點(diǎn)前的狀態(tài)(若為設(shè)置保存點(diǎn),可以直接使用rollback直接回滾到事務(wù)開始狀態(tài)),此時(shí)若我們的操作失敗、服務(wù)器奔潰,就會(huì)自動(dòng)回滾到,執(zhí)行事務(wù)前的狀態(tài)。

可以看到事務(wù)發(fā)起者異常退出時(shí),事務(wù)就會(huì)回滾到開始前狀態(tài),數(shù)據(jù)不會(huì)被持久化,采用這種事務(wù)的形式,就實(shí)現(xiàn)了原子化的概念。有很多場景在這里不可能全部演示,你可以自己嘗試,使用ctrl+\可以模擬服務(wù)退出commit提交事務(wù)
當(dāng)我們提交事務(wù)后,數(shù)據(jù)就會(huì)永久保存,再次回滾就不會(huì)回到之前了

只要事務(wù)發(fā)起者commit后數(shù)據(jù)機(jī)會(huì)持久化,即使再次回滾數(shù)據(jù)依然保存。
我們不是開啟自動(dòng)提交了嗎?我什么異常退出后,事務(wù)無法自動(dòng)提交呢?
MySQL要求,手動(dòng)發(fā)起的事務(wù),必須要手動(dòng)提交,而自動(dòng)提交是幫助默認(rèn)發(fā)起的事務(wù)做提交的,下面我們來驗(yàn)證一下。
2.3 單挑sql和事務(wù)的關(guān)系
關(guān)閉自動(dòng)提交 set autocommit=0

可以看到,當(dāng)關(guān)閉自動(dòng)提交后,事務(wù)發(fā)起者向表中插入數(shù)據(jù)時(shí),若MySQL服務(wù)異常退出,數(shù)據(jù)會(huì)回滾到之前狀態(tài)。這是因?yàn)樵贛ySQL中,單條SQL語句默認(rèn)也會(huì)被當(dāng)作事務(wù)處理,其提交依賴自動(dòng)提交功能。當(dāng)關(guān)閉自動(dòng)提交后,即便執(zhí)行單條SQL(本質(zhì)也是事務(wù)范疇),因未手動(dòng)提交,異常退出時(shí)就無法完成提交,最終觸發(fā)回滾 。
相反的,在正常情況下,自動(dòng)提交方法是打開的,那么即使mysql崩潰了,它數(shù)據(jù)也是會(huì)自動(dòng)提交成功的(所以執(zhí)行的所有sql本質(zhì)都是事務(wù)??!只不過進(jìn)行了自動(dòng)提交)。
總結(jié)
- 只要輸入 begin 或者 start transaction,事務(wù)便必須要通過 commit 提交,才會(huì)持久化,與是否設(shè)置 set autocommit 無關(guān)。
- 事務(wù)可以 手動(dòng)回滾,同時(shí),當(dāng)操作異常,MySQL 會(huì) 自動(dòng)回滾。
- 對于 InnoDB,每一條 SQL 語言都默認(rèn)封裝成事務(wù),自動(dòng)提交(select 有特殊情況,因?yàn)?MySQL 有 MVCC)。
- 從上面的例子,我們能看到事務(wù)本身的 原子性(回滾),持久性(commit)。
- 如果沒有設(shè)置 保存點(diǎn),也可以回滾,只能回滾到事務(wù)的開始,直接使用 rollback(前提是事務(wù)還沒有提交)。
- 如果一個(gè)事務(wù)被 提交了(commit),則 不可以回退(rollback)。
- 可以選擇回退到 哪個(gè)保存點(diǎn)。
- InnoDB 支持事務(wù),MyISAM 不支持事務(wù)。
- 開始事務(wù)可以使用 start transaction 或者 begin。
三、事務(wù)隔離級別
首先需要明確:MySQL服務(wù)作為網(wǎng)絡(luò)服務(wù)器,可能同時(shí)被多個(gè)客戶端進(jìn)程(線程)訪問,且訪問以事務(wù)形式進(jìn)行。一個(gè)事務(wù)可能包含多條SQL,因此任何事務(wù)都存在三個(gè)階段:
- 執(zhí)行前
- 執(zhí)行中
- 執(zhí)行后
而原子性的核心,是讓用戶層要么看到事務(wù)執(zhí)行前的狀態(tài),要么看到執(zhí)行后的狀態(tài)。若執(zhí)行中出現(xiàn)問題,可隨時(shí)通過回滾撤銷操作。因此,單個(gè)事務(wù)對用戶呈現(xiàn)的核心特性就是原子性。
但需注意:所有事務(wù)都有執(zhí)行過程,當(dāng)多個(gè)事務(wù)并發(fā)執(zhí)行多條SQL時(shí),仍可能出現(xiàn)互相影響——例如,多個(gè)事務(wù)同時(shí)訪問同一張表甚至同一行數(shù)據(jù)。
而有些情況不會(huì)對操作造成影響(如:并發(fā)讀取數(shù)據(jù)),有些情況則會(huì)導(dǎo)致結(jié)果出錯(cuò)(如:一端刪除、一端查找可能引發(fā)執(zhí)行錯(cuò)誤)。
- 因此,數(shù)據(jù)庫中為保證事務(wù)執(zhí)行過程盡量不受干擾,存在一個(gè)重要特征:隔離性;
- 同時(shí),數(shù)據(jù)庫允許事務(wù)受不同程度的干擾,由此衍生出另一種重要特征:隔離級別。
3.1 隔離級別
讀未提交(Read Uncommitted)
- 核心特征:所有事務(wù)可看到其他事務(wù)未提交的執(zhí)行結(jié)果。
- 問題:相當(dāng)于無隔離性,會(huì)引發(fā)臟讀、幻讀、不可重復(fù)讀等所有并發(fā)問題。 (上面的測試就是使用的這個(gè)隔離級別)
讀提交(Read Committed)
- 核心特征:事務(wù)僅能看到其他事務(wù)已提交的修改(滿足隔離性的基礎(chǔ)定義)。
- 問題:會(huì)導(dǎo)致不可重復(fù)讀(同一事務(wù)中多次
select可能得到不同結(jié)果)。 - 默認(rèn)情況:是大多數(shù)數(shù)據(jù)庫的默認(rèn)隔離級別(非 MySQL 默認(rèn))。
可重復(fù)讀(Repeatable Read)
- 核心特征:確保同一事務(wù)中,多次讀取數(shù)據(jù)時(shí)看到的數(shù)據(jù)行一致。
- 問題:仍可能存在幻讀。
- 默認(rèn)情況:MySQL 的默認(rèn)隔離級別。
串行化(Serializable)
- 核心特征:事務(wù)的最高隔離級別,通過強(qiáng)制事務(wù)排序避免沖突,解決幻讀問題(原理是對讀取的數(shù)據(jù)行加共享鎖)。
- 問題:可能導(dǎo)致超時(shí)和鎖競爭,性能極低。
- 使用場景:實(shí)際生產(chǎn)中基本不使用(隔離級別過于嚴(yán)格)。
3.2 隔離級別的操作
3.2.1查看隔離級別
查看全局隔離級別
select @@global.transaction_isolation;

查看當(dāng)前會(huì)話隔離級別
select @@session.transaction_isolation; select @@transaction_isolation;

3.2.2 設(shè)置隔級別
設(shè)置當(dāng)前會(huì)話 or 全局隔離級別語法:
set [ session | global ] transaction isolation level { read uncommitted | read committed | repeatable read | serializable}
設(shè)置當(dāng)前會(huì)話的隔離級別
set session transaction isolation level read committed;

設(shè)置全局的隔離級別
set global transaction isolation level serializable;

可以看到全局隔離級別的改變,不會(huì)對以創(chuàng)建的會(huì)話產(chǎn)生作用,而他的作用是設(shè)置新建的會(huì)話。也就是說重啟mysql后就會(huì)變成全局的隔離級別
四、隔離級別的具體表現(xiàn)
4.1 讀未提交
讀未提交:幾乎不施加鎖機(jī)制,雖執(zhí)行效率高,但存在嚴(yán)重問題
其核心問題是臟讀(dirty read):一個(gè)事務(wù)在執(zhí)行過程中,能讀到另一個(gè)未提交事務(wù)的更新(或其他操作)—— 因?yàn)槭聞?wù)執(zhí)行有過程性,只要一個(gè)用戶修改了表,即便未執(zhí)行 commit,其他用戶無需等待提交就能立即看到這些未確認(rèn)的操作。
這正是“讀未提交”的本質(zhì):事務(wù)只要執(zhí)行了SQL(未提交),其對表的操作就會(huì)被其他事務(wù)可見。

臟讀具體來說:
- 事務(wù)A(發(fā)起者) 對數(shù)據(jù)做了修改(如更新某條記錄),但尚未執(zhí)行
commit(可能處于執(zhí)行中或未完成); - 事務(wù)B(并發(fā)者) 在自己的事務(wù)中讀取到了事務(wù)A未提交的修改結(jié)果;
- 若事務(wù)A因異常(如代碼錯(cuò)誤、數(shù)據(jù)庫崩潰等)發(fā)生回滾,其修改會(huì)被撤銷,數(shù)據(jù)恢復(fù)到初始狀態(tài);
- 此時(shí)事務(wù)B之前讀取到的“未提交數(shù)據(jù)”就成了無效的錯(cuò)誤數(shù)據(jù),基于該數(shù)據(jù)的后續(xù)操作(如計(jì)算、決策、更新)也會(huì)隨之出錯(cuò)。
這種“讀到臨時(shí)且可能被回滾的數(shù)據(jù)”的現(xiàn)象,正是臟讀。
4.2 讀提交
為方便截取我先把表清空

在讀提交隔離級別中:
- 事務(wù)發(fā)起者未提交時(shí),事務(wù)并發(fā)者看不到其操作;
- 一旦發(fā)起者執(zhí)行 commit 提交,即使并發(fā)者的事務(wù)未結(jié)束,也會(huì)立即看到提交后的修改。
這會(huì)導(dǎo)致 不可重復(fù)讀(non-repeatable read) 問題:同一事務(wù)內(nèi),相同的讀取操作在不同時(shí)間點(diǎn)(事務(wù)仍在執(zhí)行中)返回不同結(jié)果。
從邏輯上,一個(gè)事務(wù)提交后其他事務(wù)能看到最新數(shù)據(jù)是合理的,但站在并發(fā)事務(wù)的一致性角度,同一事務(wù)內(nèi)多次讀取應(yīng)保持一致,否則會(huì)破壞事務(wù)內(nèi)數(shù)據(jù)的穩(wěn)定性——這正是不可重復(fù)讀的問題核心。
示例:
- 事務(wù)A(并發(fā)者)啟動(dòng),讀取到
account表中id=1的余額為1000元。 - 事務(wù)B(發(fā)起者)啟動(dòng),將
id=1的余額改為2000元并提交。 - 事務(wù)A未結(jié)束,再次讀取
id=1的余額,結(jié)果變?yōu)?2000元——同一事務(wù)內(nèi)兩次讀取結(jié)果不同,即不可重復(fù)讀。
4.3 可重復(fù)讀
可重復(fù)度是MySQL的默認(rèn)隔離級別

可重復(fù)讀(MySQL 默認(rèn)隔離級別)的核心特性是:同一事務(wù)在執(zhí)行期間,多次讀取數(shù)據(jù)會(huì)保持一致,直到自身事務(wù)提交后,才能看到其他事務(wù)的修改結(jié)果。
例如:終端A在事務(wù)中執(zhí)行
INSERT操作,終端B在自身事務(wù)周期內(nèi)多次查看,均不受終端A未提交或已提交數(shù)據(jù)的影響,這符合可重復(fù)讀的特點(diǎn)。
但多數(shù)數(shù)據(jù)庫的可重復(fù)讀存在一個(gè)問題:無法屏蔽其他事務(wù)新插入(
INSERT)的數(shù)據(jù)。
原因在于:隔離性通常通過對已存在數(shù)據(jù)加鎖實(shí)現(xiàn),而待插入的數(shù)據(jù)因尚未存在,無法被傳統(tǒng)鎖機(jī)制覆蓋。因此,同一事務(wù)內(nèi)多次查詢時(shí),可能會(huì)讀到其他事務(wù)新插入的記錄,出現(xiàn)“多次查找結(jié)果不一致、新增了未預(yù)期的記錄”的現(xiàn)象,這被稱為 幻讀(phantom read)。
關(guān)鍵差異:MySQL 的可重復(fù)讀(RR 級別)通過 Next-Key 鎖(間隙鎖 + 行鎖) 解決了幻讀問題——不僅鎖定已有數(shù)據(jù)行,還鎖定數(shù)據(jù)間隙,防止其他事務(wù)在間隙中插入新記錄,從而保證事務(wù)內(nèi)讀取結(jié)果的穩(wěn)定性。
4.4 串行化
前面三個(gè)隔離級別主要特點(diǎn)是:一方在修改數(shù)據(jù)(CUD操作),一方在讀取數(shù)據(jù)(R操作),但是面對雙方都在修改數(shù)據(jù)的場景(CUD操作),我們就必須通過加鎖來實(shí)現(xiàn)了
對所有事務(wù)操作全部加鎖,進(jìn)行事務(wù)的串行化,但是只要串行化,效率很低,幾乎完全不會(huì)被采用。

可以看到在串行化隔離級別下,并發(fā)的事務(wù)查詢并不會(huì)受到鎖的約束,但如果想要對數(shù)據(jù)做修改,sql就會(huì)被阻塞(阻塞時(shí)間過長就會(huì)報(bào)錯(cuò))。

當(dāng)有一方提交事務(wù)后,鎖就會(huì)被釋放,阻塞的sql就會(huì)再次執(zhí)行。
4.5 總結(jié)
隔離級別越嚴(yán)格,數(shù)據(jù)安全性越高,但數(shù)據(jù)庫并發(fā)性能越低,實(shí)際應(yīng)用中需在兩者間尋找平衡。
- 不可重復(fù)讀的重點(diǎn)是修改和刪除:同一條件下,兩次讀取同一數(shù)據(jù),結(jié)果值不同。
- 幻讀的重點(diǎn)是新增:同一條件下,兩次讀取的記錄數(shù)量不同。
從實(shí)例中可看出,事務(wù)存在長事務(wù)、短事務(wù)的概念。事務(wù)間的互相影響,在并行執(zhí)行且均未提交時(shí)尤為顯著。

一致性(Consistency)
- 核心定義:事務(wù)執(zhí)行結(jié)果必須使數(shù)據(jù)庫從一個(gè)一致性狀態(tài)切換到另一個(gè)一致性狀態(tài)。當(dāng)數(shù)據(jù)庫僅包含所有成功提交事務(wù)的結(jié)果時(shí),處于一致性狀態(tài);若因系統(tǒng)中斷導(dǎo)致未完成事務(wù)的修改寫入數(shù)據(jù)庫,則數(shù)據(jù)庫會(huì)處于不一致狀態(tài)。
- 保障關(guān)系:一致性通過原子性(事務(wù)要么全成、要么全滾)提供基礎(chǔ)保障。
- 技術(shù)支撐:從技術(shù)層面,一致性(C)通過原子性(A)、隔離性(I)、持久性(D) 共同保障(即 ACID 中的 AID 支撐 C)。
后面這篇文章是對隔離性更深層次的介紹
到此這篇關(guān)于Mysql數(shù)據(jù)庫事務(wù)全解析:概念、操作與隔離級別的文章就介紹到這了,更多相關(guān)Mysql事務(wù)隔離級別內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis集成MySQL使用游標(biāo)查詢處理大批量數(shù)據(jù)方式
這篇文章主要介紹了Mybatis集成MySQL使用游標(biāo)查詢處理大批量數(shù)據(jù)方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
MySQL數(shù)據(jù)庫的觸發(fā)器和事務(wù)
這篇文章主要介紹了MySQL數(shù)據(jù)庫的觸發(fā)器和事務(wù),觸發(fā)器是SQL?server提供給程序員和數(shù)據(jù)分析員來保證數(shù)據(jù)完整性的一種方法,它是與表事件相關(guān)的特殊的存儲(chǔ)過程,是由事件來觸發(fā)2022-08-08
python 連接數(shù)據(jù)庫mysql解壓版安裝配置及遇到問題
今天學(xué)習(xí)python連接數(shù)據(jù)庫,就想安裝一下mysql數(shù)據(jù)庫,沒想到小小的數(shù)據(jù)庫也遇到了不少挫折,所以我就把自己的安裝過程以及問題寫出來分享給大家,需要的朋友可以參考下2019-06-06
IDEA 鏈接Mysql數(shù)據(jù)庫并執(zhí)行查詢操作的完整代碼
這篇文章主要介紹了IDEA 鏈接Mysql數(shù)據(jù)庫并執(zhí)行查詢操作的完整代碼,代碼不難,詳細(xì)大家看完本文肯定有意向不到的收獲,感興趣的朋友跟隨小編一起看看吧2021-05-05
解決MySQL this is incompatible with s
本文主要介紹了解決MySQL this is incompatible with sql_mode=only_full_group_by 問題,出現(xiàn)這個(gè)問題是因?yàn)?對于GROUP BY操作,如果在SELECT中的列,沒有在GROUP BY中出現(xiàn),那么這個(gè)SQL是不合法的,因?yàn)榱胁辉贕ROUP BY從句中,下面就來了解一下2024-08-08

