MySQL的事務(wù)機(jī)制和鎖(InnoDB引擎、MVCC多版本并發(fā)控制技術(shù))

一磨确、事務(wù)(數(shù)據(jù)庫的事務(wù)都通用的定義)

1.1 事務(wù)定義

事務(wù)是由一步或幾步數(shù)據(jù)庫操作序列組成邏輯執(zhí)行單元沽甥,這系列操作要么全部執(zhí)行,要么全部放棄執(zhí)行乏奥。事務(wù)通常以?BEGIN TRANSACTION?開始摆舟,以COMMIT?或?ROLLBACK?操作結(jié)束:

COMMIT?即提交,提交事務(wù)中所有的操作邓了、事務(wù)正常結(jié)束恨诱;

ROLLBACK?即回滾,撤銷已做的所有操作驶悟,回滾到事務(wù)開始的狀態(tài)胡野。

1.2 事務(wù)的四種特性

ACID:原子性,一致性痕鳍,隔離性硫豆,持久性。

ACID屬性含義

原子性(Atomicity)指事物在邏輯上是不可分割的操作單元笼呆,所有語句要么都執(zhí)行熊响,要么都撤銷執(zhí)行。

一致性(Consistent)一個(gè)事務(wù)本質(zhì)是將數(shù)據(jù)從一種一致性狀態(tài)轉(zhuǎn)換到另一種一致性狀態(tài)诗赌,具體取決于現(xiàn)實(shí)生活的邏輯汗茄。(比如轉(zhuǎn)賬,A轉(zhuǎn)給B铭若,操作前后A+B的錢是不變的)

隔離性(Isolation)隔離性是針對(duì)并發(fā)事務(wù)而言的洪碳,同時(shí)處理多個(gè)事務(wù)的時(shí)候,數(shù)據(jù)庫的事務(wù)提供了不同的隔離級(jí)別來保證正確叼屠。

持久性(Durable)事務(wù)一旦提交瞳腌,對(duì)于數(shù)據(jù)的修改是持久性的,數(shù)據(jù)更新的結(jié)果已經(jīng)從內(nèi)存轉(zhuǎn)存到外部存儲(chǔ)器镜雨,即使系統(tǒng)故障嫂侍,已提交的數(shù)據(jù)更新也不會(huì)丟失。

這四個(gè)特性在沒有并發(fā)的時(shí)候顯然很容易滿足荚坞,但是在并發(fā)處理事務(wù)的情況下挑宠,可能會(huì)帶來一些問題

問題含義

丟失更新(Lost Update)當(dāng)兩個(gè)或多個(gè)事務(wù)操作同一行,后面的事務(wù)修改的值會(huì)覆蓋前面的事務(wù)修改的值颓影。

臟讀(Dirty Reads)一個(gè)事務(wù)讀到了被另一個(gè)事務(wù)修改各淀,但尚未提交的事務(wù)。當(dāng)一個(gè)事務(wù)正在多次修改一個(gè)數(shù)據(jù)诡挂,而這一系列修改還沒有最后提交揪阿,另一個(gè)并發(fā)事務(wù)來讀取了疗我,就會(huì)導(dǎo)致錯(cuò)誤。也就是另一個(gè)事務(wù)讀到了臟數(shù)據(jù)南捂。

不可重復(fù)讀(Non-Repeatable Reads)一個(gè)事務(wù)操作的過程里吴裤,先讀取一個(gè)數(shù)據(jù),后來又讀取溺健,而兩次讀出的數(shù)據(jù)值不一致麦牺。就是因?yàn)橹虚g被別的事務(wù)修改了。

幻讀(Phantom Reads)一個(gè)事務(wù)按照相同的查詢條件查兩次鞭缭,第一次查出了A集合剖膳,第二次卻不是了,因?yàn)槠渌聞?wù)插入了數(shù)據(jù)岭辣,正好滿足這個(gè)事務(wù)的查詢條件吱晒。

注意事項(xiàng):

臟讀和不可重復(fù)讀的區(qū)別:臟讀讀到的臟數(shù)據(jù)是另一個(gè)事務(wù)沒有提交的數(shù)據(jù),但是不可重復(fù)讀讀到錯(cuò)誤數(shù)據(jù)是因?yàn)榱硪粋€(gè)事務(wù)把數(shù)據(jù)修改并提交了沦童;

幻讀和不可重復(fù)讀的區(qū)別:幻讀和不可重復(fù)讀都是讀到了另一個(gè)事務(wù)提交的數(shù)據(jù)仑濒,但是不可重復(fù)讀是兩個(gè)事務(wù)針對(duì)同一個(gè)數(shù)據(jù)項(xiàng),而幻讀針對(duì)的是一個(gè)數(shù)據(jù)整體(數(shù)據(jù)條目)

為了解決上述提到的事務(wù)并發(fā)問題偷遗,數(shù)據(jù)庫提供一定的事務(wù)隔離機(jī)制來解決這個(gè)問題墩瞳。

數(shù)據(jù)庫的事務(wù)隔離越嚴(yán)格,并發(fā)副作用越小氏豌,但付出的代價(jià)也就越大喉酌,因?yàn)槭聞?wù)隔離實(shí)質(zhì)上就是使用事務(wù)在一定程度上“串行化” 進(jìn)行,這顯然與“并發(fā)” 是矛盾的泵喘。

隔離級(jí)別中文事務(wù)解決問題

RU:READ UNCOMMITED未提交讀(很少使用泪电,基本沒有解決問題)丟失更新

RC:READ COMMITED提交讀。顧名思義纪铺,保證一個(gè)事務(wù)只能看見另一個(gè)事務(wù)已經(jīng)提交的事務(wù)的結(jié)果相速。丟失更新+臟讀

RR:REPEATEABLE READ(Innodb默認(rèn))可重復(fù)讀。顧名思義霹陡,解決了第三個(gè)并發(fā)問題:不可重復(fù)讀和蚪。丟失更新+臟讀+不可重復(fù)讀

S:SERIALIZABLE序列化止状。通過強(qiáng)制事務(wù)排序來讓他們串行執(zhí)行烹棉。(也很少使用)本質(zhì)上是給每個(gè)數(shù)據(jù)行都加上了共享鎖四個(gè)問題都解決了

從上往下,隔離級(jí)別越來越高怯疤,但是代價(jià)肯定越來越大浆洗,真正選擇的時(shí)候需要斟酌,可以看到集峦,要想真正解決幻讀問題伏社,需要隔離級(jí)別為 S抠刺。

注意:

事實(shí)上 Mysql 的 InnoDB 通過 MVCC (Multi-Version Concurrent Control,多版本并發(fā)控制)機(jī)制解決了不可重復(fù)讀的基礎(chǔ)上摘昌,又解決了幻讀的的問題速妖。

二、MySQL的鎖(結(jié)合 InnoDB引擎)

2.1 背景

對(duì)于數(shù)據(jù)庫事務(wù)的并發(fā)控制技術(shù)有很多聪黎,基于鎖罕容、基于時(shí)間戳、基于MVCC的并發(fā)控制稿饰、基于MVCC的可串行化快照隔離等锦秒。

而我們討論的概念,是MySQL的事務(wù)喉镰,再具體一些旅择,是 InnoDB 支持的事務(wù)。

InnoDB是支持ACID的侣姆,而MySQL用InnoDB作為自己的默認(rèn)存儲(chǔ)引擎生真,事務(wù)管理是MySQL Server實(shí)現(xiàn)框架和接口定義,而InnoDB提供具體的事務(wù)操作和并發(fā)控制铺敌,所以MySQL 的事務(wù)模型汇歹,主要是指 MySQL 的InnoDB的事務(wù)管理部分。

InnoDB 使用鎖和 MVCC 技術(shù)來實(shí)現(xiàn)并發(fā)事務(wù)的訪問控制技術(shù)偿凭。

其中产弹,鎖是并發(fā)控制的基礎(chǔ),在此基礎(chǔ)之上弯囊,實(shí)現(xiàn)了 MVCC 機(jī)制痰哨,用以提高基于鎖的方式帶來的低效率問題。

有了這個(gè)概念匾嘱,我們下面分別討論MVCC兩個(gè)內(nèi)容斤斧。

MyISAM 默認(rèn)使用的是表鎖;

InnoDB 支持行級(jí)鎖霎烙,加上 MySQL Server 支持表級(jí)鎖撬讽,所以結(jié)合事務(wù)的時(shí)候,使用 InnoDB 引擎悬垃,系統(tǒng)就默認(rèn)使用的是行級(jí)鎖游昼。

里我們討論的鎖的實(shí)現(xiàn),是為了事務(wù)的并發(fā)控制尝蠕,所以都是在使用 InnoDB 引擎下的情況烘豌,那么有一些概念可能在其他引擎下的實(shí)現(xiàn)也是類似的。

2.2 鎖的分類

從對(duì)數(shù)據(jù)操作的粒度分 :

表鎖:操作時(shí)看彼,會(huì)鎖定整個(gè)表廊佩。(MySQL Server)

行鎖:操作時(shí)囚聚,會(huì)鎖定當(dāng)前操作行。(也叫Record Lock标锄,記錄鎖)顽铸,實(shí)際上他是在索引上的記錄之鎖,因?yàn)?InnoDB 的表的組織結(jié)構(gòu)是通過 B+ 樹索引料皇。

從對(duì)數(shù)據(jù)操作的類型分:

讀鎖(共享鎖跋破、S鎖):針對(duì)同一份數(shù)據(jù),多個(gè)讀操作可以同時(shí)進(jìn)行而不會(huì)互相影響瓶蝴。

寫鎖(排它鎖毒返、X鎖):當(dāng)前操作沒有完成之前,它會(huì)阻斷其他寫鎖和讀鎖舷手。

意向共享鎖(IS):事務(wù)想要獲取一張表中某幾行的共享鎖

意向排它鎖(IX):事務(wù)想要獲取一張表中的某幾行的排它鎖

前兩個(gè)很容易理解拧簸,他們的兼容情況是:只有 S 鎖和 S 鎖是兼容的,其他的組合都是互斥的男窟。

因?yàn)殒i的粒度不同盆赤,這就允許事務(wù)表級(jí)和行級(jí)的鎖可以同時(shí)存在,所以 InnoDB 支持了額外的一種鎖叫歉眷,意向鎖(Intention Lock):

問題:innodb的意向鎖有什么作用牺六?

mysql官網(wǎng)上對(duì)于意向鎖的解釋中有這么一句話

“The main purpose of IX and IS locks is to show that someone is locking a row, or going to lock a row in the table.”

意思是說加意向鎖的目的是為了表明某個(gè)事務(wù)正在鎖定一行或者將要鎖定一行。

那么汗捡,意向鎖的作用就是“表明”加鎖的意圖淑际,可是為什么要表明這個(gè)意圖呢?

如果僅僅鎖定一行僅僅需要加一個(gè)鎖扇住,那么就直接加鎖就好了春缕,這里要表明加鎖意圖的原因是因?yàn)橐i定一行不僅僅是要加一個(gè)鎖,而是要做一系列操作嗎艘蹋?

①在mysql中有表鎖锄贼,LOCK TABLE my_tabl_name READ; 用讀鎖鎖表,會(huì)阻塞其他事務(wù)修改表數(shù)據(jù)女阀。LOCK

TABLE my_table_name WRITe; 用寫鎖鎖表宅荤,會(huì)阻塞其他事務(wù)讀和寫。

②Innodb引擎又支持行鎖浸策,行鎖分為共享鎖冯键,一個(gè)事務(wù)對(duì)一行的共享只讀鎖。排它鎖的榛,一個(gè)事務(wù)對(duì)一行的排他讀寫鎖琼了。

③這兩中類型的鎖共存的問題考慮這個(gè)例子:

事務(wù)A鎖住了表中的一行逻锐,讓這一行只能讀夫晌,不能寫雕薪。之后,事務(wù)B申請(qǐng)整個(gè)表的寫鎖晓淀。如果事務(wù)B申請(qǐng)成功所袁,那么理論上它就能修改表中的任意一行,這與A持有的行鎖是沖突的凶掰。

數(shù)據(jù)庫需要避免這種沖突燥爷,就是說要讓B的申請(qǐng)被阻塞,直到A釋放了行鎖懦窘。

數(shù)據(jù)庫要怎么判斷這個(gè)沖突呢前翎?

step1:判斷表是否已被其他事務(wù)用表鎖鎖表

step2:判斷表中的每一行是否已被行鎖鎖住。

注意step2畅涂,這樣的判斷方法效率實(shí)在不高港华,因?yàn)樾枰闅v整個(gè)表。

于是就有了意向鎖午衰。在意向鎖存在的情況下立宜,事務(wù)A必須先申請(qǐng)表的意向共享鎖,成功后再申請(qǐng)一行的行鎖臊岸。在意向鎖存在的情況下橙数, 上面的判斷可以改成

step1:不變

step2:發(fā)現(xiàn)表上有意向共享鎖,說明表中有些行被共享行鎖鎖住了帅戒,因此灯帮,事務(wù)B申請(qǐng)表的寫鎖會(huì)被阻塞。

注意:申請(qǐng)意向鎖的動(dòng)作是數(shù)據(jù)庫完成的逻住,就是說施流,事務(wù)A申請(qǐng)一行的行鎖的時(shí)候,數(shù)據(jù)庫會(huì)自動(dòng)先開始申請(qǐng)表的意向鎖鄙信,不需要我們程序員使用代碼來申請(qǐng)瞪醋。

總結(jié):為了實(shí)現(xiàn)多粒度鎖機(jī)制(白話:為了表鎖和行鎖都能用)

那么這四個(gè)鎖在一起之后我們看看他們的兼容性:

2.3 其他鎖

InnoDB里還有幾個(gè)鎖:

間隙鎖(Gap Locks):兩個(gè)索引項(xiàng)之間的間隔、稱為間隙装诡,把這個(gè)間隙看作一個(gè)對(duì)象银受,在此對(duì)象上加鎖,就是間隙鎖鸦采。這個(gè)鎖是從加鎖的對(duì)象角度定義的鎖宾巍,所以和表、行是同一個(gè)角度的鎖渔伯。

Next-Key鎖(Next-Key Locks):行級(jí)鎖+間隙鎖共同組成顶霞。

Insert Intention Locks:基于間隙鎖,專門用于 Insert 操作。

間隙鎖會(huì)在 RC 隔離級(jí)別的某些情況下使用选浑,在 RR 隔離級(jí)別下蓝厌,間隙鎖會(huì)和行級(jí)鎖合并成 Next-key 鎖使用。(記住這一點(diǎn))

2.4 鎖的施加細(xì)則

對(duì)于各種 MySQL 語句來說古徒,InnoDB 對(duì)他們提供了事務(wù)操作的支持拓提,這樣的支持就是通過并發(fā)控制的鎖來完成的。(當(dāng)然隧膘,還有補(bǔ)充的 MVCC 技術(shù)代态,后面再說)

細(xì)則如下,(參考《數(shù)據(jù)庫事務(wù)處理的藝術(shù)》這本書里對(duì)官方文檔的總結(jié)疹吃,只選擇了常用的命令):

* SELECT...FOR UPDATE 或 SELECT...LOCK IN SHARE MODE:首先對(duì)掃描過的行加鎖(實(shí)際上是對(duì)索引的記錄上加鎖)蹦疑,如果掃描過的行不滿足 WHERE 條件則釋放鎖(但是有時(shí)候,鎖的釋放不及時(shí)比如 UNION 操作下被掃描過的行可能會(huì)被放到臨時(shí)表里萨驶,那就直到查詢結(jié)束才會(huì)釋放鎖)必尼;

* ALTER TABLE ... LOCK [=]?{DEFAULT | NONE | SHARED | EXCLUSIVE}:在指定的表上施加讀鎖或者排他鎖;

* CREATE TABLE...SELECT...:其中的 SELECT 操作符合 SELECT 語句的枷鎖規(guī)則篡撵,只是不能帶有 FOR UPDATE 子句判莉;

* DELETE FROM... WHERE...:在索引項(xiàng)上加排他的 Next-key 鎖;

* INSERT:在被插入的索引上加記錄鎖(意向鎖)育谬;

* INSERT... ON DUPLICATE KEY UPDATE:在被插入的索引項(xiàng)上加排他 Next-key 鎖券盅;

* INSERT INTO T SELECT ... FROM S WHERE ...:對(duì)于被插入到 T 中的元組,在對(duì)應(yīng)的索引項(xiàng)上施加排他記錄鎖膛檀。如果隔離級(jí)別是 RC 锰镀,則在表 S 對(duì)應(yīng)的索引項(xiàng)上不加鎖,這是一個(gè)一致性讀操作咖刃;否則加上共享 Next-Key 鎖泳炉;

* SELECT ... LOCK IN SHARE MODE:在索引上施加共享 Next-Key 鎖;

* SELECT ... FROM ... FOR UPDATE:在索引項(xiàng)上施加排他 Next-Key 鎖嚎杨,這樣的鎖會(huì)阻塞上一種”SELECT ... LOCK IN SHARE MODE“操作花鹅,但不會(huì)阻塞下一種?”SELECT ... FROM“?這樣的一致性讀操作;

* SELECT ... FROM 通常作為一個(gè)一致性讀操作枫浙,不需要加任何鎖刨肃。但是如果隔離級(jí)別是 S,那么也要在索引項(xiàng)上加對(duì)應(yīng)的共享 Next-Key 鎖箩帚;

* UPDATE ... WHERE ...:在索引項(xiàng)上施加排他的 Next-Key 鎖真友。

*?如果一個(gè)表上定義了外鍵約束,那么在出發(fā)約束條件被檢查的元組對(duì)應(yīng)的索引項(xiàng)上紧帕,任何操作都會(huì)施加共享Next-Key 鎖盔然。

LOCK TABLES 命令是在表級(jí)鎖,這個(gè)實(shí)現(xiàn)是MySQL Server層的實(shí)現(xiàn),InnoDB 則不會(huì)操作愈案,那么如果 InnoDB 不知道 MySQL Server設(shè)置了表級(jí)鎖挺尾,就還可能出現(xiàn)一個(gè)死鎖問題,實(shí)踐中需要注意刻帚。

三、InnoDB 的MVCC原理

在基于鎖的并發(fā)控制的基礎(chǔ)之上涩嚣,實(shí)現(xiàn)了 MVCC 技術(shù)崇众。

首先還是強(qiáng)調(diào)一點(diǎn),MVCC 技術(shù)本身思想從名字就可以看的出來航厚,就是通過多個(gè)版本進(jìn)行并發(fā)的控制顷歌。那么并發(fā)控制技術(shù),是需要配合其他的并發(fā)控制技術(shù)來具體實(shí)現(xiàn)幔睬,這里我們講的 InnoDB 的 MVCC 原理眯漩,就是基于鎖的。

3.1 日志

日志是保證事務(wù)的原子性麻顶、持久性的重要技術(shù)之一赦抖,在?MVCC?的實(shí)現(xiàn)中也是要用到的,這里簡單介紹一下辅肾,對(duì)于每一個(gè) SQL 操作队萤,都不是一下子執(zhí)行完成,因此數(shù)據(jù)的狀態(tài)都要變化矫钓,那么把這個(gè)過程記錄下來要尔,出現(xiàn)問題進(jìn)行”回放“,就能應(yīng)對(duì)事物的原子性和持久性新娜。

需要記錄的數(shù)據(jù)通常包括:

事務(wù)標(biāo)識(shí):比如事務(wù) id赵辕;

數(shù)據(jù)項(xiàng)的標(biāo)識(shí)

舊值:數(shù)據(jù)項(xiàng)被修改之前的值概龄,又稱為 前像还惠;

新值:數(shù)據(jù)項(xiàng)被修改之后的值,又稱為后像私杜。

一系列的 SQL 操作過程變成一個(gè)序列吸重,這就是日志,數(shù)據(jù)庫引擎在具體實(shí)現(xiàn)的時(shí)候會(huì)把日志放到日志緩存區(qū)歪今,然后刷出到外存嚎幸,存放到日志文件。

日志文件一般分為?REDO?日志和?UNDO?日志:

REDO 日志記錄事務(wù)的標(biāo)識(shí)寄猩、數(shù)據(jù)項(xiàng)的標(biāo)識(shí)和 新值嫉晶;

UNDO 日志記錄事務(wù)的標(biāo)識(shí)、數(shù)據(jù)項(xiàng)的標(biāo)識(shí)和 舊值。(InnoDB 的 MVCC 用到的是 UNDO log)

3.2 InnoDB 的MVCC

因?yàn)?InnoDB的多版本替废,指的是行(元組)級(jí)別的版本箍铭,在每行(或者每個(gè)元組、每條記錄)上椎镣,都有一些和并發(fā)诈火、回滾相關(guān)的隱含字段,分別為:

DB_TRX_ID:很好理解状答,就是 id冷守,表示上一個(gè)執(zhí)行(insert | update)操作的事務(wù)。至于delete操作惊科,InnoDB 認(rèn)為是一個(gè) update 操作拍摇,不過會(huì)更新一個(gè)另外的刪除位,將行表示為deleted馆截。并非真正刪除充活。

DB_ROLL_PTR:就是pointer,回滾指針蜡娶,指向的就是一個(gè)舊版本混卵。那么其實(shí)指向的是當(dāng)前記錄行的 undo log 信息,是舊版本的數(shù)據(jù)位于回滾段中的位置窖张,通過這個(gè)指針能夠找到舊版本淮菠;

DB_ROW_ID:隨著新行插入而單調(diào)遞增的行 ID,和 MVCC 關(guān)系不大荤堪。

在回滾段里的 UNDO 日志分為兩種:

INSERT UNDO logs:插入到回滾段中的日志合陵,僅用于事務(wù)提交時(shí)使用,當(dāng)事務(wù)提交澄阳,則插入 UNDO 日志里的內(nèi)容被清除拥知;

UPDATE UNDO logs:被用于一致性無鎖讀,為一致性讀提供快照隔離下的可被讀取的老版本數(shù)據(jù)碎赢。當(dāng)沒有需要滿足一致性讀的快照時(shí)低剔,一些老版本數(shù)據(jù)才能被清理。

以上肮塞,實(shí)現(xiàn)的原理基本告一段落襟齿,但是 InnoDB 的實(shí)現(xiàn)層面,還有另一個(gè)數(shù)據(jù)結(jié)構(gòu)枕赵,就是?Read View?快照猜欺。

Read View(讀視圖),跟快照拷窜、snapshot 是一個(gè)概念开皿,可以看作事務(wù)的生命周期里面的一段涧黄,而不同的快照就是不同的段。在源碼層面赋荆,他是一個(gè)類笋妥,名叫 ReadView,這里面的內(nèi)容窄潭,重點(diǎn)有兩個(gè):

一個(gè)就是保存了快照的左右邊界

另一個(gè)是提供了如何判斷當(dāng)前行(元組)的可見性的標(biāo)志春宣。

3.3 和 MVCC 有關(guān)的額外兩個(gè)概念

快照讀(snapshot read):普通的 select 語句(不包括 select ... lock in share mode, select ... for update)。也就是不加鎖的非阻塞讀嫉你,所以在串行級(jí)別下的快照讀會(huì)退化成當(dāng)前讀月帝。他是基于多版本的,那么快照讀可能讀到的并不一定是數(shù)據(jù)的最新版本均抽,而有可能是之前的歷史版本嫁赏。

當(dāng)前讀(current read):select ... lock in share mode其掂,select ... for update油挥,insert,update款熬,delete 語句深寥。(為什么叫當(dāng)前讀?就是它讀取的是記錄的最新版本贤牛,讀取時(shí)還要保證其他并發(fā)事務(wù)不能修改當(dāng)前記錄惋鹅,會(huì)對(duì)讀取的記錄進(jìn)行加鎖。)

對(duì)于 InnoDB 的 MVCC

實(shí)現(xiàn)殉簸,很多博客和書都是架空寫的原理闰集,看了《高性能MySQL》,里面也寫的是基于每個(gè)事務(wù)操作的時(shí)候給該行添加兩個(gè)版本號(hào)(當(dāng)前版本號(hào)般卑、刪除版本號(hào))武鲁,然后事務(wù)操作的時(shí)候根據(jù)版本號(hào)來判定是否執(zhí)行完畢還是回滾等規(guī)則。現(xiàn)在看來并不是這樣蝠检,但是從上面提到的沐鼠、源碼里實(shí)際增加的字段來看,思想是大概類似的叹谁,不過實(shí)現(xiàn)的機(jī)制更加復(fù)雜饲梭。

四、MVCC 原理總結(jié)

4.1 總結(jié) MVCC 原理

到這一步焰檩,概念有點(diǎn)多憔涉,我們來梳理一下。

首先析苫,事務(wù)的概念有了监氢,事務(wù)的特性隨著概念出來:ACID布蔗。

那么,并發(fā)事務(wù)如果不加控制浪腐,就會(huì)存在問題纵揍,按處理的難易程度從低到高:丟失更新-臟讀-不可重復(fù)讀-幻讀

于是议街,數(shù)據(jù)庫如果實(shí)現(xiàn)事務(wù)泽谨,就要保證特性,解決對(duì)應(yīng)的問題特漩,應(yīng)運(yùn)而生四個(gè)隔離級(jí)別:RU-RC-RR-S吧雹。

因?yàn)槭聞?wù)是在存儲(chǔ)引擎層實(shí)現(xiàn)的,所以接下來討論了基于InnoDB 引擎的 MySQL 事務(wù)的實(shí)現(xiàn):

額外的幾個(gè)字段涂身;

基于?Undo log雄卷;

結(jié)合?Read View?(快照)。

相關(guān)概念:鎖的分類:表鎖蛤售、行鎖丁鹉。讀鎖、寫鎖悴能、意向鎖揣钦。間隙鎖(Gap Locks)、Next-Key鎖漠酿;

相關(guān)概念:鎖的實(shí)施細(xì)則冯凹;

相關(guān)概念:基于鎖的并發(fā)版本控制,結(jié)合 MVCC炒嘲。如果沒有 MVCC 宇姚,四個(gè)并發(fā)問題,除了讀讀不用加鎖夫凸,讀寫浑劳、寫讀、寫寫都不能并發(fā)執(zhí)行寸痢,否則就會(huì)產(chǎn)生問題呀洲,效率低下,有了MVCC啼止,讀寫和寫讀可以并發(fā)執(zhí)行道逗。那 MVCC 都用到了什么呢?

現(xiàn)在我們回過頭看一下MVCC機(jī)制工作的兩個(gè)隔離級(jí)別:RC 和 RR :

RC献烦,提交讀滓窍,要解決臟讀的問題,保證一個(gè)事務(wù)讀取到的一定是另一個(gè)事務(wù)的已經(jīng)提交的結(jié)果巩那,而不能是未提交的結(jié)果吏夯。那么此蜈,對(duì)于 RC級(jí)別來說,務(wù)中噪生,每次快照讀都會(huì)新生成一個(gè)快照和Read View裆赵,保證每個(gè)事務(wù)可以看到別的事務(wù)提交的更新。(關(guān)于具體的實(shí)現(xiàn)還有相應(yīng)的算法跺嗽,可見性之類的)战授;

RR可重復(fù)讀桨嫁,要解決不可重復(fù)讀的問題植兰,保證快照讀生成Read View時(shí),Read View會(huì)記錄此時(shí)所有其他活動(dòng)事務(wù)的快照璃吧,這些事務(wù)的修改對(duì)于當(dāng)前事務(wù)都是不可見的楣导。

4.2 MVCC 解決幻讀問題了嗎?

RR級(jí)別下畜挨,沒有完全解決幻讀的問題筒繁。

我們回憶幻讀的概念:一個(gè)事務(wù)按照相同的查詢條件查兩次,第一次查出了A集合朦促,第二次卻不是了膝晾,因?yàn)槠渌聞?wù)插入了數(shù)據(jù)栓始,正好滿足這個(gè)事務(wù)的查詢條件务冕。

那么對(duì)于上面的 MVCC 原理,基于快照讀的情況:

事務(wù) A 開始后幻赚,執(zhí)行普通 select 語句禀忆,創(chuàng)建了快照;

事務(wù) B 執(zhí)行 insert 語句落恼;

事務(wù) A 再執(zhí)行普通 select 語句箩退,得到的還是之前 B 沒有 insert 過的數(shù)據(jù),因?yàn)檫@時(shí)候 A 讀的數(shù)據(jù)是符合快照可見性條件的數(shù)據(jù)佳谦。

這是解決了幻讀問題的戴涝。

但是考慮這種情況:

事務(wù)A執(zhí)行的不是普通 select 語句,而是 select ... for update 等語句钻蔑,根據(jù)上面的定義啥刻,事務(wù) A 是當(dāng)前讀,每次語句執(zhí)行的時(shí)候都是獲取的最新數(shù)據(jù)咪笑。

那么 B 執(zhí)行 insert語句可帽;

A 再次查詢的時(shí)候,就可能會(huì)查到多一條數(shù)據(jù)窗怒,產(chǎn)生幻讀映跟。

這個(gè)時(shí)候就要出場(chǎng)我們?cè)谇懊娴逆i分類部分的一行紅字蓄拣,另外兩個(gè)鎖:間隙鎖和 Next-key Locks。

間隙鎖在 RR 級(jí)別發(fā)揮作用努隙,結(jié)合行級(jí)鎖稱為 Next-key locks球恤,解決幻讀問題。具體的算法可能很復(fù)雜荸镊,原理就是鎖定范圍的設(shè)置加上了間隙碎捺,這樣插入操作肯定是沒辦法進(jìn)行的,因此就不會(huì)存在其他事務(wù)的插入操作導(dǎo)致幻讀了贷洲。

到這里我們可以得出結(jié)論收厨,MySQL 里完全解決幻讀的方法有兩個(gè):

直接使用 S 隔離等級(jí)完全串行化;

RR 的隔離級(jí)別結(jié)合 MVCC 機(jī)制优构,還要結(jié)合 間隙鎖诵叁。

五、其他

關(guān)于數(shù)據(jù)庫的鎖钦椭,服務(wù)器層面的實(shí)現(xiàn)默認(rèn)是有表級(jí)別的鎖拧额,沒有行鎖(書上沒有提這個(gè)點(diǎn),但是網(wǎng)上也有說法講服務(wù)器層面也實(shí)現(xiàn)了行鎖)彪腔;

不同的存儲(chǔ)引擎層面又以自己的方式實(shí)現(xiàn)了不同粒度級(jí)別的鎖侥锦,因此選擇引擎不同的時(shí)候,我們使用的鎖德挣,了解的原理都是基于引擎的恭垦,另一方面,鎖總和事務(wù)聯(lián)系在一起討論格嗅,不同的存儲(chǔ)引擎是否支持事務(wù)又是不一樣的番挺,所以應(yīng)該是把server層關(guān)于這一塊忽略掉了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末屯掖,一起剝皮案震驚了整個(gè)濱河市玄柏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贴铜,老刑警劉巖粪摘,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異绍坝,居然都是意外死亡徘意,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門陷嘴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來映砖,“玉大人,你說我怎么就攤上這事灾挨∫赝耍” “怎么了竹宋?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長地技。 經(jīng)常有香客問我蜈七,道長,這世上最難降的妖魔是什么莫矗? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任飒硅,我火速辦了婚禮,結(jié)果婚禮上作谚,老公的妹妹穿的比我還像新娘三娩。我一直安慰自己,他們只是感情好妹懒,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布雀监。 她就那樣靜靜地躺著,像睡著了一般眨唬。 火紅的嫁衣襯著肌膚如雪会前。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天匾竿,我揣著相機(jī)與錄音瓦宜,去河邊找鬼。 笑死岭妖,一個(gè)胖子當(dāng)著我的面吹牛临庇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播区转,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼苔巨,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼版扩!你這毒婦竟也來了废离?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤礁芦,失蹤者是張志新(化名)和其女友劉穎蜻韭,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柿扣,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肖方,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了未状。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俯画。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖司草,靈堂內(nèi)的尸體忽然破棺而出艰垂,到底是詐尸還是另有隱情泡仗,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布猜憎,位于F島的核電站娩怎,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏胰柑。R本人自食惡果不足惜截亦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望柬讨。 院中可真熱鬧崩瓤,春花似錦、人聲如沸踩官。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卖鲤。三九已至肾扰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蛋逾,已是汗流浹背集晚。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留区匣,地道東北人偷拔。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像亏钩,于是被迫代替她去往敵國和親莲绰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345