MySQL事務(wù)(transaction)看這篇就足夠了
MySQL事務(wù)處理(TransAction)
思考了很久,決定寫(xiě)一篇關(guān)于mysql事務(wù)(transaction)的博客,一來(lái)嘛,因?yàn)樽罱趶?fù)習(xí)mysql的相關(guān)知識(shí),幫自己回顧總結(jié)一下,其次就是想把這篇博客分享給大家,如果你才剛剛開(kāi)始學(xué)習(xí)mysql,那么希望這篇博客對(duì)你有一點(diǎn)啟發(fā);亦或者你早已是一個(gè)mysql老油條,這篇博客也會(huì)使你對(duì)mysql事務(wù)有一個(gè)更深的印象。
首先,什么是事務(wù)呢?
事務(wù)就是由單獨(dú)單元的一個(gè)或多個(gè)sql語(yǔ)句組成,在這個(gè)單元中,每個(gè)sql語(yǔ)句都是相互依賴(lài)的。而整個(gè)單獨(dú)單元是作為一個(gè)不可分割的整體存在,類(lèi)似于物理當(dāng)中的原子(一種不可分割的最小單位)。
往通俗的講就是,事務(wù)就是一個(gè)整體,里面的內(nèi)容要么都執(zhí)行成功,要么都不成功。不可能存在部分執(zhí)行成功而部分執(zhí)行不成功的情況。
就是說(shuō)如果單元中某條sql語(yǔ)句一旦執(zhí)行失敗或者產(chǎn)生錯(cuò)誤,那么整個(gè)單元將會(huì)回滾(返回最初狀態(tài))。所有受到影響的數(shù)據(jù)將返回到事務(wù)開(kāi)始之前的狀態(tài),但是如果單元中的所有sql語(yǔ)句都執(zhí)行成功的話(huà),那么該事務(wù)也就被順利執(zhí)行。
大家都知道,我們的數(shù)據(jù)都是通過(guò)各種不同技術(shù)的存儲(chǔ)引擎來(lái)引導(dǎo)存儲(chǔ)的,不同的存儲(chǔ)引擎,都有各自的特點(diǎn)。在mysql中,常見(jiàn)的存儲(chǔ)引擎有innodb、myisam,memory等。其中innodb支持事務(wù)(transaction),而myisam,memory等不支持事務(wù)。
可以通過(guò)
show engines;語(yǔ)句來(lái)查看mysql支持的存儲(chǔ)引擎
一、事務(wù)的四個(gè)特性(ACID)【面試??柬?xiàng)】
- 原子性(Atomicity):指事務(wù)是一個(gè)不可分割的最小工作單位,事務(wù)中的操作只有都發(fā)生和都不發(fā)生兩種情況
- 一致性(Consistency):事務(wù)必須使數(shù)據(jù)庫(kù)從一個(gè)一致?tīng)顟B(tài)變換到另外一個(gè)一致?tīng)顟B(tài),舉一個(gè)栗子,李二給王五轉(zhuǎn)賬50元,其事務(wù)就是讓李二賬戶(hù)上減去50元,王五賬戶(hù)上加上50元;一致性是指其他事務(wù)看到的情況是要么李二還沒(méi)有給王五轉(zhuǎn)賬的狀態(tài),要么王五已經(jīng)成功接收到李二的50元轉(zhuǎn)賬。而對(duì)于李二少了50元,王五還沒(méi)加上50元這個(gè)中間狀態(tài)是不可見(jiàn)的。
- 隔離性(Isolation):一個(gè)事務(wù)的執(zhí)行不能被其他事務(wù)干擾,即一個(gè)事務(wù)內(nèi)部的操作及使用的數(shù)據(jù)對(duì)并發(fā)的其他事務(wù)是隔離的,并發(fā)執(zhí)行的各個(gè)事務(wù)之間不能互相干擾。
- 持久性(Durability):一個(gè)事務(wù)一旦提交成功,它對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)的改變將是永久性的,接下來(lái)的其他操作或故障不應(yīng)對(duì)其有任何影響。
二、事務(wù)的分類(lèi)
事務(wù)分為隱式事務(wù)和顯式事務(wù)兩種。我們的DML語(yǔ)句(insert、update、delete)就是隱式事務(wù)。
1.隱式事務(wù):該事務(wù)沒(méi)有明顯的開(kāi)啟和結(jié)束標(biāo)記,它們都具有自動(dòng)提交事務(wù)的功能;不妨思考一下,update語(yǔ)句修改數(shù)據(jù)時(shí),是不是對(duì)表中數(shù)據(jù)進(jìn)行改變了,它的本質(zhì)其實(shí)就相當(dāng)于一個(gè)事務(wù)。
舉一個(gè)栗子:張三同學(xué)買(mǎi)了一個(gè)csdn定制保溫杯花了99元,是不是就是update語(yǔ)句對(duì)字段name為張三的同學(xué)的余額balance進(jìn)行減99元的處理呢?代碼如下:

2.顯示事務(wù):該事務(wù)具有明顯的開(kāi)啟和結(jié)束標(biāo)記;也是本文重點(diǎn)要講的東西。使用顯式事務(wù)的前提是你得先把自動(dòng)提交事務(wù)的功能給禁用。禁用自動(dòng)提交功能就是設(shè)置autocommit變量值為0(0:禁用 1:開(kāi)啟)
先查看一下當(dāng)前的autocommit變量值,發(fā)現(xiàn)當(dāng)前處于開(kāi)啟自動(dòng)提交事務(wù)的狀態(tài)

禁用自動(dòng)提交事務(wù)的功能并查看當(dāng)前狀態(tài)

三、開(kāi)啟事務(wù)的步驟
假設(shè)t_account表已經(jīng)存在
#步驟一:開(kāi)啟事務(wù)(可選) start transaction; #步驟二:編寫(xiě)事務(wù)中的sql語(yǔ)句(insert、update、delete) #這里實(shí)現(xiàn)一下"李二給王五轉(zhuǎn)賬"的事務(wù)過(guò)程 update t_account set balance = 50 where vname = "李二"; update t_account set balance = 130 where vname = "王五"; #步驟三:結(jié)束事務(wù) commit; #提交事務(wù) # rollback; #回滾事務(wù):就是事務(wù)不執(zhí)行,回滾到事務(wù)執(zhí)行前的狀態(tài)
運(yùn)行結(jié)果:

四、事務(wù)并發(fā)時(shí)出現(xiàn)的問(wèn)題
但是呢,因?yàn)槟骋豢滩豢赡芸傊挥幸粋€(gè)事務(wù)在運(yùn)行,可能出現(xiàn)A在操作t_account表中的數(shù)據(jù),B也同樣在操作t_account表,那么就會(huì)出現(xiàn)并發(fā)問(wèn)題,對(duì)于同時(shí)運(yùn)行的多個(gè)事務(wù),當(dāng)這些事務(wù)訪問(wèn)數(shù)據(jù)庫(kù)中相同的數(shù)據(jù)時(shí),如果沒(méi)有采用必要的隔離機(jī)制,就會(huì)發(fā)生以下各種并發(fā)問(wèn)題。
- ??臟讀:對(duì)于兩個(gè)事務(wù)T1,T2,T1讀取了已經(jīng)被T2更新但還沒(méi)有被提交的字段之后,若T2回滾,T1讀取的內(nèi)容就是臨時(shí)且無(wú)效的
- ??不可重復(fù)讀 :對(duì)于兩個(gè)事務(wù)T1,T2,T1讀取了一個(gè)字段,然后T2更新了該字段之后,T1在讀取同一個(gè)字段,值就不同了
- ?? 幻讀:對(duì)于兩個(gè)事務(wù)T1,T2,T1在A表中讀取了一個(gè)字段,然后T2又在A表中插入了一些新的數(shù)據(jù)時(shí),T1再讀取該表時(shí),就會(huì)發(fā)現(xiàn)神不知鬼不覺(jué)的多出幾行了…
所以,為了避免以上出現(xiàn)的各種并發(fā)問(wèn)題,我們就必然要采取一些手段。mysql數(shù)據(jù)庫(kù)系統(tǒng)提供了四種事務(wù)的隔離級(jí)別,用來(lái)隔離并發(fā)運(yùn)行各個(gè)事務(wù),使得它們相互不受影響,這就是數(shù)據(jù)庫(kù)事務(wù)的隔離性。
五、事務(wù)的隔離級(jí)別
mysql中的四種事務(wù)隔離級(jí)別如下:
1. read uncommitted(讀未提交數(shù)據(jù)):允許事務(wù)讀取未被其他事務(wù)提交的變更。(臟讀、不可重復(fù)讀和幻讀的問(wèn)題都會(huì)出現(xiàn))。
2. read committed(讀已提交數(shù)據(jù)):只允許事務(wù)讀取已經(jīng)被其他事務(wù)提交的變更。(可以避免臟讀,但不可重復(fù)讀和幻讀的問(wèn)題仍然可能出現(xiàn))
3.repeatable read(可重復(fù)讀):確保事務(wù)可以多次從一個(gè)字段中讀取相同的值,在這個(gè)事務(wù)持續(xù)期間,禁止其他事務(wù)對(duì)這個(gè)字段進(jìn)行更新(update)。(可以避免臟讀和不可重復(fù)讀,但幻讀仍然存在)
4. serializable(串行化):確保事務(wù)可以從一個(gè)表中讀取相同的行,在這個(gè)事務(wù)持續(xù)期間,禁止其他事務(wù)對(duì)該表執(zhí)行插入、更新和刪除操作,所有并發(fā)問(wèn)題都可避免,但性能十分低下(因?yàn)槟悴煌瓿删投疾豢梢耘?,效率太低?/p>
了解: oracle支持兩種事務(wù)隔離級(jí)別:read committed、serializable。
oracle默認(rèn)的事務(wù)隔離級(jí)別是:read committed。
mysql的默認(rèn)事務(wù)隔離級(jí)別是:repeatable read。
一個(gè)事務(wù)與其他事務(wù)隔離的程度稱(chēng)為隔離級(jí)別。數(shù)據(jù)庫(kù)規(guī)定了多種事務(wù)隔離級(jí)別,不同隔離級(jí)別對(duì)應(yīng)不同的干擾程度,隔離級(jí)別越高,數(shù)據(jù)一致性就越好,但并發(fā)性就越差。
這里通過(guò)一個(gè)例子向大家簡(jiǎn)單介紹一下并發(fā):
一個(gè)人?? 在邊開(kāi)車(chē)?? 邊打電話(huà)??,首先,人只有一個(gè)大腦(cpu),但是在同一時(shí)刻他卻在執(zhí)行2件事情,其實(shí)內(nèi)部就是靠他的大腦在不斷的切換執(zhí)行,之所以人民警察?? 不允許司機(jī)開(kāi)車(chē)時(shí)打電話(huà),就是怕人腦在那一瞬間切換不過(guò)來(lái),從而導(dǎo)致交通事故的發(fā)生,并發(fā)和這個(gè)例子是差不多的意思。但在這里,電腦cpu可比人腦快多了,所以出錯(cuò)的概率也相對(duì)來(lái)說(shuō)小很多。
接一下,演示一下在幾種不同的事務(wù)隔離級(jí)別下所發(fā)生的不同情況?? 。
在演示之前呢,還需要知道如何查看和設(shè)置事務(wù)的隔離級(jí)別
查看當(dāng)前的事務(wù)隔離級(jí)別通過(guò)
tx_isolation變量或者transaction_isolation(版本8.0以上使用);語(yǔ)法:select @@tx_isolation;
注意:在mysql8.0之后,就已經(jīng)拋棄了tx_isolation變量了,而是用
transaction_isolation變量代替了。語(yǔ)法:select @@transaction_isolation;
#設(shè)置當(dāng)前mysql連接的隔離級(jí)別: set session transaction isolation level read uncommitted; #設(shè)置數(shù)據(jù)庫(kù)系統(tǒng)的全局的隔離級(jí)別: set global transaction isolation level read uncommitted;
注意:當(dāng)前mysql連接的隔離級(jí)別和mysql全局的隔離級(jí)別的區(qū)別是什么?
如果只設(shè)置當(dāng)前的隔離級(jí)別,也就是session,那么另外一個(gè)并發(fā)的“mysqy程序”的隔離級(jí)別不會(huì)受到當(dāng)前連接的影響,而是保持默認(rèn)的repeatable read。
但是如果是設(shè)置全局的事務(wù)隔離級(jí)別,則整個(gè)mysql數(shù)據(jù)庫(kù)(包括所有打開(kāi)的mysql程序連接)的隔離級(jí)別都會(huì)隨之改變,除非服務(wù)器重啟,不然就不會(huì)恢復(fù)默認(rèn)了。
兩者僅僅一詞之差,其效果卻天差地別。
好,了解完如何設(shè)置事務(wù)的隔離級(jí)別之后,下面將正式進(jìn)入…
呃呃呃… 等一下
這里的講解主要是為了知道在并發(fā)的環(huán)境下,不同的事務(wù)隔離級(jí)別所表現(xiàn)出的不同特點(diǎn),那么自然還是要先模擬一下并發(fā)環(huán)境
這里打開(kāi)兩個(gè)獨(dú)立的mysql數(shù)據(jù)庫(kù)連接(mysql程序1和mysql程序2),用來(lái)模擬并發(fā)環(huán)境

咳咳咳… 正片開(kāi)始
同志們打起精神認(rèn)真看?。。?/p>
- read uncommitted(讀未提交)
首先,我們需要先將兩個(gè)會(huì)話(huà)的事務(wù)隔離級(jí)別都設(shè)置為read uncommitted;語(yǔ)句如下:

read uncommitted可以讀到其他事務(wù)還沒(méi)提交的變更,這里舉例:程序2對(duì)t_account表中的數(shù)據(jù)進(jìn)行更改,看程序1多次查詢(xún)的結(jié)果是否一致。

可以看到程序2改變了t_account表中的vname字段,將李二改為了張三。但是程序1呢,連續(xù)2個(gè)select查詢(xún)語(yǔ)句的結(jié)果竟然不一致,估計(jì)現(xiàn)在程序1的表情和你手機(jī)里的第三個(gè)表情包一樣。這就是read uncommitted隔離級(jí)別的特點(diǎn)。
不管你事務(wù)是否提交,只要數(shù)據(jù)發(fā)生改變我就可以察覺(jué)到…嘻嘻。是不是感覺(jué)它很強(qiáng)大。什么事情都逃不過(guò)它的法眼。
接下來(lái)要看的是read committed(讀已提交)
- read committed(讀已提交)
測(cè)試前一定要記住設(shè)置事務(wù)的隔離級(jí)別為read committed;并且禁用自動(dòng)提交事務(wù)【設(shè)置autocommit為0】(后面的每個(gè)測(cè)試都是一樣的)
# 設(shè)置事務(wù)隔離級(jí)別為read committed set session transaction isolation level read committed; # 禁用自動(dòng)提交事務(wù)功能 set autocommit = 0; #接下來(lái)的 repeatable read 隔離級(jí)別和 serializable 隔離級(jí)別也是同樣的操作

以上例子實(shí)現(xiàn)了王五為張三轉(zhuǎn)賬的事務(wù),可以看到程序2中事務(wù)提交前與提交后對(duì)程序1中的查詢(xún)語(yǔ)句產(chǎn)生的影響,前2個(gè)查詢(xún)是事務(wù)沒(méi)提交的結(jié)果,最后一個(gè)查詢(xún)是事務(wù)提交后的結(jié)果。 相信上面的例子已經(jīng)很充分的詮釋了read committed的特點(diǎn)。
- repeatable read(可重復(fù)讀)
該隔離級(jí)別為mysql的默認(rèn)隔離級(jí)別;它對(duì)某字段進(jìn)行操作時(shí),其他事務(wù)禁止操作該字段。它總能保持你讀取的數(shù)據(jù)是一致的。
以下代碼中,程序1模擬"王五向張三轉(zhuǎn)賬30元",程序2則在程序1在處理事務(wù)時(shí),對(duì)張三的余額進(jìn)行清空處理

因?yàn)楫?dāng)前的事務(wù)隔離級(jí)別為repeatable read級(jí)別,所以在程序1操作t_account表時(shí),程序2是無(wú)權(quán)對(duì)t_account表進(jìn)行任何操作,如果強(qiáng)行操作的話(huà),就會(huì)發(fā)生error (錯(cuò)誤)
“ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction”;
其中文意思是:“超過(guò)鎖定等待超時(shí);嘗試重新啟動(dòng)事務(wù)”
只有當(dāng)程序1對(duì)t_account表操作完成后(結(jié)束事務(wù)后),程序2才可以對(duì)t_account表進(jìn)行操作。

- serializable(串行化)
該隔離模式下執(zhí)行的事務(wù)在對(duì)某表進(jìn)行操作期間,禁止其他所有事務(wù)對(duì)該表進(jìn)行任何操作
如果強(qiáng)行操作也會(huì)報(bào)錯(cuò)(和上面那個(gè)錯(cuò)誤一致),因?yàn)閟erializable用的相對(duì)比較少,這里就不做演示了。同學(xué)們理解了就好。
- 事務(wù)的保存點(diǎn)(回滾點(diǎn))
回滾點(diǎn)表示的就是使事務(wù)回滾到指定回滾點(diǎn)
語(yǔ)法: savepoint 節(jié)點(diǎn)名稱(chēng) ;
注意:保存點(diǎn)只允許搭配rollback回滾來(lái)使用,不能和commit一起使用
已知表t_stu存在,其數(shù)據(jù)如下:

代碼舉例如下:
#禁用自動(dòng)提交事務(wù) set autocommit = 0; #開(kāi)啟事務(wù) start transaction; #刪除id為2的記錄 delete from t_stu where id = 2; #設(shè)置保存點(diǎn)名為AA savepoint AA; #刪除id為3的記錄 delete from t_stu where id = 3; #回滾到AA保存點(diǎn)處 rollback to AA;
運(yùn)行結(jié)果如下:

可以看到id為2的李四被刪除了,而王五卻還在,就是因?yàn)槭聞?wù)回滾到了AA處,所以id為3的那條記錄被回滾掉了。
總結(jié)
到此這篇關(guān)于MySQL事務(wù)(transaction)的文章就介紹到這了,更多相關(guān)MySQL事務(wù)transaction內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
簡(jiǎn)單了解標(biāo)準(zhǔn)SQL的update語(yǔ)句三種用法
這篇文章主要介紹了簡(jiǎn)單了解標(biāo)準(zhǔn)SQL的update語(yǔ)句三種用法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下2019-06-06
mysql表的內(nèi)連和外連實(shí)戰(zhàn)記錄
在開(kāi)發(fā)中我們的業(yè)務(wù)需求有時(shí)候是復(fù)雜的,多張表聯(lián)合查詢(xún)的時(shí)候是有多種方式的,面對(duì)不同的需求,靈活使用不同的表連接方式,這篇文章主要給大家介紹了關(guān)于mysql表內(nèi)連和外連的相關(guān)資料,需要的朋友可以參考下2024-01-01
MySQL記錄操作日志常用的幾種實(shí)現(xiàn)方法
這篇文章主要介紹了MySQL記錄操作日志常用的幾種實(shí)現(xiàn)方法,文中介紹的方法包括啟用通用查詢(xún)?nèi)罩?、二進(jìn)制日志、使用審計(jì)插件和觸發(fā)器,每種方法都有其適用場(chǎng)景和優(yōu)缺點(diǎn),選擇合適的方法可以有效跟蹤和管理數(shù)據(jù)庫(kù)操作,需要的朋友可以參考下2024-11-11
MySQL 實(shí)現(xiàn)雙向復(fù)制的方法指南
這篇文章主要介紹了MySQL 實(shí)現(xiàn)雙向復(fù)制的方法指南,本文包括:主機(jī)配置,從機(jī)配置,建立主-從復(fù)制,建立雙向復(fù)制,需要的朋友可以參考下2015-03-03
如何使Mysql自動(dòng)生成序號(hào)列,序號(hào)自動(dòng)增長(zhǎng)問(wèn)題
這篇文章主要介紹了如何使Mysql自動(dòng)生成序號(hào)列,序號(hào)自動(dòng)增長(zhǎng)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
MySQL對(duì)字符串使用STR_TO_DATE()函數(shù)詳解
文章介紹了MySQL中的STR_TO_DATE()函數(shù),用于將字符串轉(zhuǎn)換為日期時(shí)間類(lèi)型,轉(zhuǎn)換時(shí)需要注意字符串格式、日期有效性等問(wèn)題2025-03-03
PHP學(xué)習(xí)散記 2012_01_07(MySQL)
數(shù)據(jù)庫(kù)是Web大多數(shù)應(yīng)用開(kāi)發(fā)的基礎(chǔ)。如果你是用PHP,那么大多數(shù)據(jù)庫(kù)用的是MYSQL也是LAMP架構(gòu)的重要部分2012-01-01
MySQL UPDATE 語(yǔ)句一個(gè)“經(jīng)典”的坑
這篇文章主要介紹了MySQL UPDATE 語(yǔ)句一個(gè)“經(jīng)典”的坑,一個(gè)非常有意思的話(huà)題,感興趣的小伙伴可以參考下面文章的具體內(nèi)容哦2021-10-10


