淺入淺出MySQL事務(wù)

在開發(fā)Web應(yīng)用時(shí),經(jīng)常會(huì)用到InnoDB事務(wù)的特性,在一些涉及到金錢的業(yè)務(wù)上,事務(wù)可以保證資金流水不出錯(cuò),事務(wù)可以分為很多種,有扁平事務(wù)、鏈?zhǔn)聞?wù)、分布式事務(wù)等,這里只討論最簡單,也最常用的扁平事務(wù),我們經(jīng)常會(huì)提到事務(wù)的ACID特性,以下是我對(duì)ACID的理解和總結(jié)。

  • 原子性
  • 一致性
  • 隔離性
  • 持久性

原子性

原子性指的是對(duì)于一系列增刪改查的操作,要么全部執(zhí)行,要么全部不執(zhí)行,事務(wù)可通過start transaction或者begin語句顯示的開啟,commit用于顯示的提交,例如:

start transaction
update `user` set name='haha' where uid=1
insert into `user` (name) values ('小馬')
commit

對(duì)于以上兩部操作,若成功則都成功,若由于某些原因第二條語句執(zhí)行出錯(cuò),則可以執(zhí)行rollback語句,這些一般是由程序語言來控制。

一致性

一致性和原子性有很多相似的地方,我的理解是,一致性的特點(diǎn)是依賴于原子性實(shí)現(xiàn)的,什么意思呢?比如說A給B匯款1000元,這個(gè)狀態(tài)分為兩步即:

  1. 從A的賬戶扣1000元
  2. 再往B的賬戶+1000元

這兩個(gè)步驟,對(duì)于事務(wù)的原子性來說,指要么都成功,要么都失敗,原子性關(guān)注的是狀態(tài),而一致性關(guān)注的是最終的金額是否一致,即1000元不會(huì)丟失,可能理解起來會(huì)有點(diǎn)歧義,它們之間有相似的地方,一致性依賴于原子性實(shí)現(xiàn)。參考 事務(wù)隔離級(jí)別淺析。

隔離性

隔離性指的是事務(wù)在提交之前,對(duì)其它事務(wù)是不可見的,每個(gè)事務(wù)對(duì)對(duì)象的操作相對(duì)其它事務(wù)都是相互分離的,這個(gè)特性依靠鎖機(jī)制來實(shí)現(xiàn)。

持久性

持久性指的是事務(wù)一旦提交,其結(jié)果是永久性的,即使發(fā)生宕機(jī)等故障,數(shù)據(jù)庫也能將數(shù)據(jù)恢復(fù)。

事務(wù)的隔離級(jí)別

在數(shù)據(jù)庫操作中,為了解決并發(fā)數(shù)據(jù)讀寫時(shí)數(shù)據(jù)的正確性問題,提出了事務(wù)的隔離級(jí)別。數(shù)據(jù)庫的鎖也是為了構(gòu)建這些隔離級(jí)別而存在的。下面是SQL標(biāo)準(zhǔn)定義的四個(gè)隔離級(jí)別,以及可能發(fā)生臟讀、不可重復(fù)讀、幻讀的可能性,隔離級(jí)別從上往下一次遞增。

隔離級(jí)別 臟讀 不可重復(fù)讀 幻讀
未提交讀(Read uncommitted) 可能 可能 可能
已提交讀(Read committed) 不可能 可能 可能
可重復(fù)讀(Repeatable read) 不可能 不可能 可能
可串行化(Serializable ) 不可能 不可能 不可能

臟讀、不可重復(fù)讀、幻讀

要理解事務(wù)的隔離級(jí)別,首先要理解臟讀、不可重復(fù)讀和幻讀是什么意思,下面通過幾個(gè)例子來演示一下。

  • 臟讀
事務(wù)A 事務(wù)B
START TRANSACTION START TRANSACTION
update user set name='小馬' where uid=1
select * from user where uid=1
commit rollback

Read uncommitted隔離級(jí)別下,A事務(wù)和B事務(wù)同時(shí)開啟,B事務(wù)對(duì)uid=1的這行數(shù)據(jù)做了修改操作,沒有提交,但是在事務(wù)A里面已經(jīng)能被讀取到,而此時(shí)事務(wù)B執(zhí)行了rollback回滾操作,也就是說A讀到的是一行不存在的數(shù)據(jù),這種情況被稱為臟讀,只有在未提交讀的隔離級(jí)別下才會(huì)發(fā)生這種情況。

  • 可重復(fù)讀
事務(wù)A 事務(wù)B
START TRANSACTION START TRANSACTION
select * from user where uid = 1 ...
... update user set name='小馬' where uid=1
... commit
select * from user where uid = 1 ...

不可重復(fù)讀指的是在一個(gè)事務(wù)中,執(zhí)行兩次查詢操作,兩次結(jié)果是不一樣的,稱為不可重復(fù)讀,可重復(fù)讀則相反,同一個(gè)事務(wù)內(nèi),兩次讀到的數(shù)據(jù)一致。根據(jù)上面的例子,事務(wù)A和事務(wù)B同時(shí)開啟,第一次A事務(wù)通過select語句查詢uid=1的這行數(shù)據(jù),這時(shí)候B修改了這行數(shù)據(jù)并且執(zhí)行了提交操作,第二次A事務(wù)再通過select語句查詢這行數(shù)據(jù)的時(shí)候,讀取到的結(jié)果就不一樣了,所以稱為不可重復(fù)讀,這種情況在Read committed隔離級(jí)別下存在,Repeatable read級(jí)別則實(shí)現(xiàn)了可重復(fù)讀。

  • 幻讀
事務(wù)A 事務(wù)B
START TRANSACTION START TRANSACTION
select count(*) from user ...
... insert into user (name) values ('mike')
... commit
select count(*) from user ...

幻讀和不可重復(fù)讀有一些相似的地方,不可重復(fù)讀針對(duì)的是修改刪除操作,這兩種操作可通過對(duì)數(shù)據(jù)行增加一個(gè)排它鎖來解決,但是insert操作不一樣,你沒有辦法鎖住一條尚未存在的數(shù)據(jù),理論上在Repeatable read隔離級(jí)別只解決了不可重復(fù)讀問題,沒有解決幻讀問題,RR級(jí)別也是innodb默認(rèn)的隔離級(jí)別,值得慶幸的是,innodb的RR級(jí)別通過MVCC多版本并發(fā)控制,解決了在RR級(jí)別下的幻讀問題,所以理論上Innodb是完全滿足事務(wù)的ACID屬性的,想詳細(xì)了解MVCC的同學(xué)可自行搜索引擎或者看書,本文由于篇幅的原因,暫不詳細(xì)展開。

總結(jié)

理解了上面說的這些概念,就很容易理解事務(wù)的隔離級(jí)別了,下面是對(duì)事務(wù)隔離級(jí)別的總結(jié):

  • 未提交讀:允許臟讀,A事務(wù)可以讀到B事務(wù)未提交的數(shù)據(jù)
  • 已提交讀:A事務(wù)只能讀到B事務(wù)已提交的數(shù)據(jù)
  • 可重復(fù)讀:A事務(wù)對(duì)于同一行數(shù)據(jù),前后兩次讀到的數(shù)據(jù)一定是一樣的,即使B事務(wù)對(duì)它執(zhí)行了修改,且提交了,結(jié)果也不會(huì)變。
  • 串行化:完全串行化的讀,每次讀都需要獲得表級(jí)共享鎖,讀寫相互都會(huì)阻塞

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容