MySQL InnoDB 并發(fā)控制雄可,事務(wù)的實(shí)現(xiàn) 學(xué)習(xí)筆記

所在文集:數(shù)據(jù)庫(kù)


如何保證數(shù)據(jù)一致性

通過(guò)并發(fā)控制保證數(shù)據(jù)一致性的常見(jiàn)手段有:

  • 鎖(Locking)
  • 數(shù)據(jù)多版本(Multi Versioning)

如何使用普通鎖保證一致性凿傅?

  • 操作數(shù)據(jù)前,鎖住滞项,實(shí)施互斥狭归,不允許其他的并發(fā)任務(wù)操作;
  • 操作完成后文判,釋放鎖过椎,讓其他任務(wù)執(zhí)行;

普通鎖存在什么問(wèn)題戏仓?
簡(jiǎn)單的鎖住太過(guò)粗暴疚宇,連“讀任務(wù)”也無(wú)法并行亡鼠,任務(wù)執(zhí)行過(guò)程本質(zhì)上是串行的。

于是出現(xiàn)了共享鎖排他鎖

  • 共享鎖(Share Locks敷待,記為S鎖)间涵,讀取數(shù)據(jù)時(shí)加 S 鎖
    • 共享鎖之間不互斥,簡(jiǎn)記為:讀讀可以并行
  • 排他鎖(eXclusive Locks榜揖,記為X鎖)勾哩,修改數(shù)據(jù)時(shí)加 X 鎖
    • 排他鎖與任何鎖互斥,簡(jiǎn)記為:寫(xiě)讀举哟,寫(xiě)寫(xiě)不可以并行
    • 可以看到思劳,一旦寫(xiě)數(shù)據(jù)的任務(wù)沒(méi)有完成,數(shù)據(jù)是不能被其他任務(wù)讀取的妨猩,這對(duì)并發(fā)度有較大的影響

有沒(méi)有可能潜叛,進(jìn)一步提高并發(fā)呢?
即使寫(xiě)任務(wù)沒(méi)有完成壶硅,其他讀任務(wù)也可能并發(fā)威兜,這就引出了數(shù)據(jù)多版本
數(shù)據(jù)多版本是一種能夠進(jìn)一步提高并發(fā)的方法庐椒,它的核心原理是:

  • 寫(xiě)任務(wù)發(fā)生時(shí)椒舵,將數(shù)據(jù)克隆一份,以版本號(hào)區(qū)分扼睬;
  • 寫(xiě)任務(wù)操作新克隆的數(shù)據(jù)逮栅,直至提交;
  • 并發(fā)讀任務(wù)可以繼續(xù)讀取舊版本的數(shù)據(jù)窗宇,不至于阻塞;
數(shù)據(jù)多版本

如上圖:

  • 最開(kāi)始數(shù)據(jù)的版本是 V0特纤;
  • T1 時(shí)刻發(fā)起了一個(gè)寫(xiě)任務(wù)军俊,這是把數(shù)據(jù)克隆了一份,進(jìn)行修改炭剪,版本變?yōu)?V1静尼,但任務(wù)還未完成捉貌;
  • T2 時(shí)刻并發(fā)了一個(gè)讀任務(wù),依然可以讀 V0 版本的數(shù)據(jù)镰官;
  • T3 時(shí)刻又并發(fā)了一個(gè)讀任務(wù),依然不會(huì)阻塞吗货;

可以看到泳唠,數(shù)據(jù)多版本,通過(guò)“讀取舊版本數(shù)據(jù)”能夠極大提高任務(wù)的并發(fā)度宙搬。

提高并發(fā)的演進(jìn)思路笨腥,就在如此:

  • 普通鎖拓哺,本質(zhì)是串行執(zhí)行
  • 讀寫(xiě)鎖,可以實(shí)現(xiàn)讀讀并發(fā)
  • 數(shù)據(jù)多版本脖母,可以實(shí)現(xiàn)讀寫(xiě)并發(fā)

undo log

日志文件士鸥,記錄數(shù)據(jù)被 修改前 的值,用來(lái)進(jìn)行 rollback回滾谆级。
如某個(gè)事務(wù) T1烤礁,將 X 的值由 5 修改為 10,則 undo log 寫(xiě)入 <T1肥照,X脚仔,5>
對(duì)于 insert 操作,undo log 記錄新數(shù)據(jù)的 PK(ROW_ID)建峭,回滾時(shí)直接刪除玻侥;

為什么要有 undo log?
數(shù)據(jù)庫(kù)事務(wù)未提交時(shí)亿蒸,會(huì)將事務(wù)修改數(shù)據(jù)的鏡像(即修改前的舊版本)存放到 undo log 里凑兰,當(dāng)事務(wù)回滾時(shí),或者數(shù)據(jù)庫(kù)奔潰時(shí)边锁,可以利用 undo log姑食,即舊版本數(shù)據(jù),撤銷(xiāo)未提交事務(wù)對(duì)數(shù)據(jù)庫(kù)產(chǎn)生的影響茅坛。

一句話音半,undo日志用于保障,未提交事務(wù)不會(huì)對(duì)數(shù)據(jù)庫(kù)的ACID特性產(chǎn)生影響贡蓖。

磁盤(pán)上 不存在 單獨(dú)的 undo log 文件曹鸠,所有的 undo log 均存放在主 ibd 數(shù)據(jù)文件中(表空間)。
記錄先寫(xiě)入到 undo buffer斥铺,但當(dāng)緩沖滿(mǎn)的時(shí)候彻桃,undo buffer 中的內(nèi)容會(huì)也會(huì)被刷新到磁盤(pán)。

redo log

日志文件晾蜘,記錄數(shù)據(jù)被 修改后 的值邻眷,回來(lái)恢復(fù)未寫(xiě)入到磁盤(pán)文件的已成功事務(wù)更新的數(shù)據(jù)。
如某個(gè)事務(wù) T1剔交,將 X 的值由 5 修改為 10肆饶,則 redo log 寫(xiě)入 <T1,X岖常,10>

為什么要有 redo log驯镊?
數(shù)據(jù)庫(kù)事務(wù)提交后,必須將更新后的數(shù)據(jù)刷到磁盤(pán)上,以保證 ACID 特性阿宅。磁盤(pán)隨機(jī)寫(xiě)性能較低候衍,如果每次都刷盤(pán),會(huì)極大影響數(shù)據(jù)庫(kù)的吞吐量洒放。
優(yōu)化方式是蛉鹿,將修改行為先寫(xiě)到 redo log 里,再定期將數(shù)據(jù)刷到磁盤(pán)上往湿,這樣能極大提高性能妖异。
假如某一時(shí)刻,數(shù)據(jù)庫(kù)崩潰领追,還沒(méi)來(lái)得及刷盤(pán)的數(shù)據(jù)他膳,在數(shù)據(jù)庫(kù)重啟后,會(huì)重做 redo log 日志里的內(nèi)容绒窑,以保證已提交事務(wù)對(duì)數(shù)據(jù)產(chǎn)生的影響都刷到磁盤(pán)上棕孙。

一句話,redo log 用于保障些膨,已提交事務(wù)的 ACID 特性蟀俊。

磁盤(pán)上 存在 單獨(dú)的 redo log 文件。
記錄先寫(xiě)入到 redo buffer订雾,但當(dāng)緩沖滿(mǎn)的時(shí)候肢预,redo buffer 中的內(nèi)容會(huì)也會(huì)被刷新到磁盤(pán)。

rollback segment 回滾段

回滾段洼哎。在 InnoDB 中烫映,undo log 被劃分為多個(gè)段,具體某行的 undo log 就保存在某個(gè)段中噩峦,稱(chēng)為回滾段锭沟。

事務(wù)的過(guò)程

假設(shè) User 表有兩個(gè)字段 <name, salary>
InnoDB 為每行記錄都實(shí)現(xiàn)了三個(gè)隱藏字段:

  • 6 字節(jié)识补,單調(diào)遞增的行 ID(DB_ROW_ID
  • 6 字節(jié)冈钦,記錄每一行最近一次修改它的事務(wù) ID(DB_TRX_ID
  • 7 字節(jié),回滾指針(DB_ROLL_PTR)李请,記錄指向回滾段 undo log 的指針

因此實(shí)際的 User 表有五個(gè)字段 <name, salary, ID, DB_TRX_ID, DB_ROLL_PTR>

假設(shè)某一行的初始值為:<Tom, 10000, 1, NULL, NULL>

假設(shè)某一個(gè)事務(wù)里執(zhí)行了 update users set salary = 20000 where name = 'Tom'厉熟,存儲(chǔ)引擎會(huì)依次進(jìn)行如下操作:

  • 排他鎖 鎖定該行导盅。
  • 記錄 redo log,即被 修改后 的值 <Tom, 20000>
  • 記錄 undo log揍瑟,即被 修改前 的值 <Tom, 10000, 1, NULL, NULL>
  • 修改當(dāng)前數(shù)據(jù)表中的值白翻,變?yōu)?<Tom, 20000, 1, 01, DB_ROLL_PTR>,其中 DB_ROLL_PTR 指向之前 undo log 中的那一行

若 提交了 事務(wù)
只需要更改事務(wù)狀態(tài)為 COMMIT 即可,不需做其他額外的工作滤馍。

若 回滾了 事務(wù)
需要根據(jù)當(dāng)前 DB_ROLL_PTR 回滾指針 從 undo log 中找出事務(wù)修改前的版本岛琼,并恢復(fù)。

一個(gè)具體的示例

數(shù)據(jù)表 t(id PK, name)
數(shù)據(jù)為:

1, shenjian
2, zhangsan
3, lisi

此時(shí)沒(méi)有事務(wù)未提交巢株,故回滾段是空的槐瑞。

接著啟動(dòng)了一個(gè)事務(wù),并且事務(wù)處于未提交的狀態(tài)阁苞。

start trx;
delete (1, shenjian);
update set(3, lisi) to (3, xxx);
insert (4, wangwu);

可以看到:

  • 被刪除前的 (1, shenjian) 作為舊版本數(shù)據(jù)困檩,進(jìn)入了回滾段;
  • 被修改前的 (3, lisi) 作為舊版本數(shù)據(jù)那槽,進(jìn)入了回滾段悼沿;
  • 被插入的數(shù)據(jù) PK(4) 進(jìn)入了回滾段;

接下來(lái)骚灸,假如事務(wù) rollback糟趾,此時(shí)可以通過(guò)回滾段里的 undo log 回滾。



可以看到:

  • 被刪除的舊數(shù)據(jù)恢復(fù)了甚牲;
  • 被修改的舊數(shù)據(jù)也恢復(fù)了义郑;
  • 被插入的數(shù)據(jù),刪除了鳖藕;

InnoDB 是基于多版本并發(fā)控制的存儲(chǔ)引擎

InnoDB 是高并發(fā)互聯(lián)網(wǎng)場(chǎng)景最為推薦的存儲(chǔ)引擎魔慷,根本原因,就是其多版本并發(fā)控制(Multi Version Concurrency Control, MVCC)著恩。行鎖院尔,并發(fā),事務(wù)回滾等多種特性都和 MVCC 相關(guān)喉誊。
MVCC就是通過(guò)“讀取舊版本數(shù)據(jù)”來(lái)降低并發(fā)事務(wù)的鎖沖突邀摆,提高任務(wù)的并發(fā)度。

InnoDB為何能夠做到這么高的并發(fā)伍茄?
回滾段里的數(shù)據(jù)栋盹,其實(shí)是歷史數(shù)據(jù)的快照(snapshot),這些數(shù)據(jù)是不會(huì)被修改敷矫,select 可以肆無(wú)忌憚的并發(fā)讀取他們例获。

快照讀(Snapshot Read),這種一致性不加鎖的讀(Consistent Nonlocking Read)曹仗,就是 InnoDB 并發(fā)如此之高的核心原因之一榨汤。

這里的一致性是指,事務(wù)讀取到的數(shù)據(jù)怎茫,要么是事務(wù)開(kāi)始前就已經(jīng)存在的數(shù)據(jù)(當(dāng)然收壕,是其他已提交事務(wù)產(chǎn)生的),要么是事務(wù)自身插入或者修改的數(shù)據(jù)蜜宪。

什么樣的 select 是快照讀虫埂?
除非顯示加鎖,普通的select語(yǔ)句都是快照讀圃验,例如:
select * from t where id>2;

這里的顯示加鎖,非快照讀是指:
select * from t where id>2 lock in share mode;
select * from t where id>2 for update;

總結(jié):

  • 常見(jiàn)并發(fā)控制保證數(shù)據(jù)一致性的方法有鎖损谦,數(shù)據(jù)多版本;
  • 普通鎖串行闯参,讀寫(xiě)鎖讀讀并行悲立,數(shù)據(jù)多版本讀寫(xiě)并行鹿寨;
  • redo log 保證已提交事務(wù)的 ACID 特性脚草,設(shè)計(jì)思路是,通過(guò)順序?qū)懱娲S機(jī)寫(xiě)原献,提高并發(fā)馏慨;
  • undo log 用來(lái)回滾未提交的事務(wù),它存儲(chǔ)在回滾段里姑隅;
  • InnoDB 是基于 MVCC 的存儲(chǔ)引擎写隶,它利用了存儲(chǔ)在回滾段里的 undo log,即數(shù)據(jù)的舊版本讲仰,提高并發(fā)慕趴;
  • InnoDB 之所以并發(fā)高,快照讀不加鎖鄙陡;
  • InnoDB 所有普通 select 都是快照讀冕房;

引用:
Mysql InnoDB MVCC 實(shí)現(xiàn)原理
InnoDB并發(fā)如此高,原因竟然在這趁矾?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末毒费,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子愈魏,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件培漏,死亡現(xiàn)場(chǎng)離奇詭異溪厘,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)牌柄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)畸悬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人珊佣,你說(shuō)我怎么就攤上這事蹋宦。” “怎么了咒锻?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵冷冗,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我惑艇,道長(zhǎng)蒿辙,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任滨巴,我火速辦了婚禮思灌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘恭取。我一直安慰自己泰偿,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布蜈垮。 她就那樣靜靜地躺著耗跛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪窃款。 梳的紋絲不亂的頭發(fā)上课兄,一...
    開(kāi)封第一講書(shū)人閱讀 49,741評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音晨继,去河邊找鬼烟阐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛紊扬,可吹牛的內(nèi)容都是我干的蜒茄。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼餐屎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼檀葛!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起腹缩,我...
    開(kāi)封第一講書(shū)人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤屿聋,失蹤者是張志新(化名)和其女友劉穎空扎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體润讥,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡转锈,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了楚殿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撮慨。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖脆粥,靈堂內(nèi)的尸體忽然破棺而出砌溺,到底是詐尸還是另有隱情,我是刑警寧澤变隔,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布规伐,位于F島的核電站,受9級(jí)特大地震影響弟胀,放射性物質(zhì)發(fā)生泄漏楷力。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一孵户、第九天 我趴在偏房一處隱蔽的房頂上張望萧朝。 院中可真熱鬧,春花似錦夏哭、人聲如沸检柬。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)何址。三九已至,卻和暖如春进胯,著一層夾襖步出監(jiān)牢的瞬間用爪,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工胁镐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留偎血,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓盯漂,卻偏偏與公主長(zhǎng)得像颇玷,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子就缆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容