一鼻疮、什么是MVCC?
??MVCC(Multi-Version Concurrency Control)多版本并發(fā)控制琳轿,是確保在高并發(fā)下判沟,多個(gè)事務(wù)讀取數(shù)據(jù)時(shí)不加鎖也可以多次讀取相同的值。MVCC在讀已提交(READ COMMITTED)崭篡、可重復(fù)讀(REPEATABLE READ 簡稱RR)模式下才生效挪哄。
二、MVCC解決了事務(wù)的什么問題琉闪?
在并發(fā)事務(wù)下迹炼,可能會(huì)產(chǎn)生如下問題:
1.臟讀 :當(dāng)前事務(wù)讀取到其它事務(wù)未提交的數(shù)據(jù)。
2.臟寫 : 事務(wù)B提交后颠毙,將事務(wù)A提交的數(shù)據(jù)覆蓋斯入。
3.不可重復(fù)讀:在同一個(gè)事務(wù)中砂碉,不同時(shí)間段執(zhí)行相同的查詢語句,得到的結(jié)果集不相同咱扣。
4.幻讀:事務(wù)A讀取到了事務(wù)B新增的數(shù)據(jù)绽淘。
MVCC可重復(fù)讀模式下,解決了事務(wù)的臟讀闹伪、臟寫沪铭、不可重復(fù)讀等問題,但是還是存在幻讀問題偏瓤,幻讀問題可以使用間隙鎖進(jìn)行解決杀怠。
三、MVCC的實(shí)現(xiàn)原理
3.1 undo log文件
??MVCC是通過歷史操作快照的版本鏈進(jìn)行實(shí)現(xiàn)的厅克,這個(gè)版本鏈實(shí)際上就是undo log的記錄的數(shù)據(jù)赔退。
??undo log里面通過兩個(gè)隱藏字段trx_id、roll_pointer將這些數(shù)據(jù)串聯(lián)起來证舟,形成一個(gè)歷史版本鏈硕旗。當(dāng)事務(wù)需要回滾或事務(wù)需要執(zhí)行select語句時(shí),都是通過undo log里面的roll_pointer去查找對(duì)應(yīng)版本的數(shù)據(jù)女责。
3.2 read view一致性視圖
??當(dāng)事務(wù)要執(zhí)行查詢sql時(shí)漆枚,會(huì)生成一個(gè)一致性視圖,也就是read view抵知,read view由當(dāng)前所有未提交的事務(wù)ID組成的一個(gè)數(shù)組墙基,和已創(chuàng)建的最大事務(wù)id構(gòu)成。
??在這個(gè)數(shù)組中刷喜,最小的事務(wù)id稱為min_id,最大的事務(wù)id稱為max_id,當(dāng)要查詢歷史數(shù)據(jù)時(shí)残制,需要通過對(duì)比事務(wù)的id才能區(qū)分哪些版本的數(shù)據(jù)可以展示,哪些數(shù)據(jù)不能展示掖疮。
示例:read view = [100,101],300;
其中初茶,Min_id = 100、Max_id = 300氮墨。100和101都屬于未提交的活躍事務(wù)纺蛆。
在可重復(fù)讀模式下,只會(huì)在第一次執(zhí)行查詢語句時(shí)生成read view规揪,而在讀已提交模式下桥氏,每次查詢都會(huì)生成一個(gè)read view。
3.3 redo log 版本鏈對(duì)比規(guī)則
Mysql將整個(gè)版本鏈的數(shù)據(jù)劃分為三個(gè)區(qū)間:
如果trx_id < min_id猛铅,那么表示這個(gè)事務(wù)是屬于已提交的事務(wù),數(shù)據(jù)是可見的字支。
如果trx_id > max_id,那么標(biāo)識(shí)這個(gè)事務(wù)是屬于將來要開啟的,數(shù)據(jù)是不可見的堕伪。
如果min_id <= trx_id <= max_id 揖庄,就要分為兩種情況:
??a.如果trx_id在read view數(shù)組中,那么就表示當(dāng)前trx_id屬于未提交的事務(wù)欠雌,數(shù)據(jù)是不可見的蹄梢。
??b.如果trx_id不在read view數(shù)組中,那么當(dāng)前trx_id就是屬于已提交的事務(wù)富俄,數(shù)據(jù)是可見的禁炒。
??正是有了MVCC機(jī)制,讓多個(gè)事務(wù)對(duì)同一條數(shù)據(jù)進(jìn)行讀寫時(shí)霍比,不需要加鎖也不會(huì)出現(xiàn)讀寫沖突幕袱。事務(wù)A每次讀的都是歷史版本的快照,而事務(wù)B修改的數(shù)據(jù)悠瞬,則會(huì)稱為這個(gè)版本鏈的最新數(shù)據(jù)们豌,通過上面的對(duì)比規(guī)則,其它事務(wù)是不會(huì)讀取到最新的已修改的值浅妆。
??但是MVCC沒有解決幻讀問題望迎,對(duì)于其它事務(wù)新增的數(shù)據(jù),雖然讀不到凌外,但是仍然可以感知到新增的數(shù)據(jù)擂煞,比如對(duì)新增數(shù)據(jù)進(jìn)行修改操作。在可重復(fù)讀模式下趴乡,可以使用間隙鎖進(jìn)行解決幻讀問題。
3.4 間隙鎖 Gap Lock
間隙鎖可以理解成范圍鎖蝗拿,鎖的是兩個(gè)值之間的范圍晾捏,其它事務(wù)無法對(duì)范圍內(nèi)的數(shù)據(jù)進(jìn)行新增和修改。
比如事務(wù)A當(dāng)前的間隙鎖的值是7-10哀托,但是目前數(shù)據(jù)庫中并不存在id =8的數(shù)據(jù)惦辛,事務(wù)B想要新增id =8的數(shù)據(jù),此時(shí)是無法新增的仓手,這樣就可以解決幻讀問題胖齐。
四、數(shù)據(jù)庫中的鎖
4.1 數(shù)據(jù)庫的鎖是什么嗽冒?
在并發(fā)場(chǎng)景下呀伙,鎖是協(xié)調(diào)并發(fā)訪問資源的一種手段,通過加鎖阻塞方式添坊,讓一個(gè)資源在同一時(shí)刻只能有一個(gè)客戶端去訪問剿另。
4.2 鎖的分類
a.從性能上分
悲觀鎖:對(duì)要訪問的資源直接加鎖
樂觀鎖:通過版本對(duì)比的方式來實(shí)現(xiàn),不會(huì)對(duì)訪問的資源加鎖
b.從操作類型分
讀鎖(shared 共享鎖):針對(duì)同一行數(shù)據(jù),可以允許多個(gè)讀雨女,但是會(huì)阻塞寫鎖谚攒。
寫鎖(exclusive 排它鎖):對(duì)于同一行數(shù)據(jù),會(huì)阻塞其它事務(wù)的讀和寫氛堕。
c.從鎖的粒度上分
表鎖:開銷小馏臭、加鎖快,不會(huì)出現(xiàn)死鎖問題讼稚,鎖的粒度大括儒,并發(fā)度低。
行鎖:開銷大乱灵、加鎖慢塑崖,會(huì)出現(xiàn)死鎖問題,鎖的粒度小痛倚,并發(fā)度高规婆。
一句話總結(jié):讀鎖不會(huì)阻塞讀,會(huì)阻塞寫蝉稳;寫鎖會(huì)阻塞讀寫抒蚜。
行鎖:InnoDB中的行鎖是加在索引上的,不是針對(duì)數(shù)據(jù)行進(jìn)行加鎖耘戚。如果該索引失效嗡髓,行鎖會(huì)升級(jí)成表鎖。
五收津、總結(jié)
MVCC:
1饿这、MVCC在不加鎖的情況下,解決了并發(fā)事務(wù)的臟讀撞秋、臟寫不可重復(fù)度等問題长捧,而幻讀可以使用間隙鎖進(jìn)行解決。
2吻贿、undo log里面通過兩個(gè)隱藏字段trx_id串结、roll_pointer將歷史快照數(shù)據(jù)串聯(lián)起來,形成一個(gè)版本鏈舅列,是read view獲取數(shù)據(jù)的前提肌割。
3、read view是在第一次查詢時(shí)生成的帐要,由所有未提交的活躍事務(wù)id組成的數(shù)組和最大事務(wù)id構(gòu)成把敞。
4、通過對(duì)比事務(wù)id的大小宠叼,將數(shù)據(jù)進(jìn)行展示先巴。
鎖:
InnoDB中其爵,行鎖是加在索引上,因此要注意索引的失效問題伸蚯,否則行鎖升級(jí)成表鎖摩渺。
盡量減少條件檢索范圍,避免間隙鎖的范圍過大剂邮,降低并發(fā)力度摇幻。