自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

再有人問(wèn)你什么是MVCC,就把這篇文章發(fā)給他!

開(kāi)發(fā) 前端
MVCC,是Multiversion Concurrency Control的縮寫(xiě),翻譯過(guò)來(lái)是多版本并發(fā)控制,他也是一種并發(fā)控制的解決方案。

?一想到并發(fā)控制,很多人第一反應(yīng)就是加鎖,的確,加鎖確實(shí)是解決并發(fā)問(wèn)題最常見(jiàn)的方案。但是,其實(shí)除了加鎖以外,在數(shù)據(jù)庫(kù)領(lǐng)域,還有一種無(wú)鎖的方案可以來(lái)實(shí)現(xiàn)并發(fā)控制,那就是大名鼎鼎的MVCC。

MVCC,是Multiversion Concurrency Control的縮寫(xiě),翻譯過(guò)來(lái)是多版本并發(fā)控制,他也是一種并發(fā)控制的解決方案。    

我們知道,在數(shù)據(jù)庫(kù)中,對(duì)數(shù)據(jù)的操作主要有2中,分別是讀和寫(xiě),而在并發(fā)場(chǎng)景下,就可能出現(xiàn)以下三種情況:

  • 讀-讀并發(fā)
  • 讀-寫(xiě)并發(fā)
  • 寫(xiě)-寫(xiě)并發(fā)

我們都知道,在沒(méi)有寫(xiě)的情況下發(fā)讀-讀并?是不會(huì)出現(xiàn)問(wèn)題的,而寫(xiě)-寫(xiě)并發(fā)?這種情況比較常用的就是通過(guò)加鎖的方式實(shí)現(xiàn)。那么,讀-寫(xiě)并發(fā)則可以通過(guò)MVCC的機(jī)制解決。本文就來(lái)介紹下一下MySQL中MVCC的實(shí)現(xiàn)機(jī)制。

快照讀和當(dāng)前讀

要想搞清楚MVCC的機(jī)制,最重要的一個(gè)概念那就是快照讀。

所謂快照讀,就是讀取的是快照數(shù)據(jù),即快照生成的那一刻的數(shù)據(jù),像我們常用的普通的SELECT語(yǔ)句在不加鎖情況下就是快照讀。如:

SELECT * FROM xx_table WHERE ...

和快照讀相對(duì)應(yīng)的另外一個(gè)概念叫做當(dāng)前讀,當(dāng)前讀就是讀取最新數(shù)據(jù),所以,加鎖的 SELECT,或者對(duì)數(shù)據(jù)進(jìn)行增刪改都會(huì)進(jìn)行當(dāng)前讀,比如:

SELECT * FROM xx_table LOCK IN SHARE MODE;
SELECT * FROM xx_table FOR UPDATE;
INSERT INTO xx_table ...
DELETE FROM xx_table ...
UPDATE xx_table ...

可以說(shuō)快照讀是MVCC實(shí)現(xiàn)的基礎(chǔ),而當(dāng)前讀是悲觀(guān)鎖實(shí)現(xiàn)的基礎(chǔ)。

那么,快照讀讀到的快照是從哪里讀到的的呢?換句話(huà)說(shuō),快照是存在哪里的呢?

Undo Log

undo log是Mysql中比較重要的事務(wù)日志之一,顧名思義,undo log是一種用于回退的日志,在事務(wù)沒(méi)提交之前,MySQL會(huì)先記錄更新前的數(shù)據(jù)到 undo log日志文件里面,當(dāng)事務(wù)回滾時(shí)或者數(shù)據(jù)庫(kù)崩潰時(shí),可以利用 undo log來(lái)進(jìn)行回退。

這里面提到的存在undo log中的”更新前的數(shù)據(jù)”就是我們前面提到的快照。所以,這也是為什么很多人說(shuō)UndoLog是MVCC實(shí)現(xiàn)的重要手段的原因。

那么,一條記錄在同一時(shí)刻可能有多個(gè)事務(wù)在執(zhí)行,那么,undo log會(huì)有一條記錄的多個(gè)快照,那么在這一時(shí)刻發(fā)生SELECT要進(jìn)行快照讀的時(shí)候,要讀哪個(gè)快照呢?

這就需要用到另外幾個(gè)信息了。

隱式字段

其實(shí),數(shù)據(jù)庫(kù)中的每行記錄中,除了保存了我們自己定義的一些字段以外,還有一些重要的隱式字段的:

  • db_row_id:隱藏主鍵,如果我們沒(méi)有給這個(gè)表創(chuàng)建主鍵,那么會(huì)以這個(gè)字段來(lái)創(chuàng)建聚簇索引。
  • db_trx_id:對(duì)這條記錄做了最新一次修改的事務(wù)的ID
  • db_roll_ptr:回滾指針,指向這條記錄的上一個(gè)版本,其實(shí)他指向的就是Undo Log中的上一個(gè)版本的快照的地址。

因?yàn)槊恳淮斡涗涀兏岸紩?huì)先存儲(chǔ)一份快照到undo log中,那么這幾個(gè)隱式字段也會(huì)跟著記錄一起保存在undo log中,就這樣,每一個(gè)快照中都有有一個(gè)db_trx_id字段記錄了本次變更的事務(wù)ID,以及一個(gè)db_roll_ptr字段指向了上一個(gè)快照的地址。(db_trx_id和db_roll_ptr是重點(diǎn),后面還會(huì)用到)

這樣,就形成了一個(gè)快照鏈表:

圖片

有了undo log,又有了幾個(gè)隱式字段,我們好像還是不知道具體應(yīng)該讀取哪個(gè)快照,那怎么辦呢?

Read View

這時(shí)候就需要Read View 登場(chǎng)了,

Read View 主要來(lái)幫我們解決可見(jiàn)性的問(wèn)題的, 即他會(huì)來(lái)告訴我們本次事務(wù)應(yīng)該看到哪個(gè)快照,不應(yīng)該看到哪個(gè)快照。

在 Read View 中有幾個(gè)重要的屬性:

  • trx_ids,系統(tǒng)當(dāng)前未提交的事務(wù) ID 的列表。
  • low_limit_id,未提交的事務(wù)中最大的事務(wù) ID。
  • up_limit_id,未提交的事務(wù)中最小的事務(wù) ID。
  • creator_trx_id,創(chuàng)建這個(gè) Read View 的事務(wù) ID。

每開(kāi)啟一個(gè)事務(wù),我們都會(huì)從數(shù)據(jù)庫(kù)中獲得一個(gè)事務(wù) ID,這個(gè)事務(wù) ID 是自增長(zhǎng)的,通過(guò) ID 大小,我們就可以判斷事務(wù)的時(shí)間順序。

那么,一個(gè)事務(wù)應(yīng)該看到哪些快照,不應(yīng)該看到哪些快照該如何判斷呢?

其實(shí)原則比較簡(jiǎn)單,那就是事務(wù)ID大的事務(wù)應(yīng)該能看到事務(wù)ID小的事務(wù)的變更結(jié)果,反之則不能!舉個(gè)例子:

假如當(dāng)前有一個(gè)事務(wù)3想要對(duì)某條記錄進(jìn)行一次快照讀的時(shí)候,他會(huì)先創(chuàng)建一個(gè)Read View,并且把當(dāng)前所有還未提交的事務(wù)的信息記錄下來(lái)。比如up_limit_id = 2,low_limit_id = 5,trx_ids= [2,4,5],creator_trx_id= 6

圖片

我們前面說(shuō)過(guò),每一條記錄上都有一個(gè)隱式字段db_trx_id記錄對(duì)這條記錄做了最新一次修改的事務(wù)的ID,如db_trx_id = 3;

那么接下來(lái),數(shù)據(jù)庫(kù)會(huì)拿這條記錄db_trx_id和Read View進(jìn)行可見(jiàn)性比較。

如果db_trx_id<up_limit_id,則說(shuō)明,在Read View中所有未提交的事務(wù)創(chuàng)建之前,db_trx_id = 3的這個(gè)事務(wù)就已經(jīng)提交了,并且在這期間,并沒(méi)有新的事務(wù)提交。所有,這條記錄對(duì)當(dāng)前事務(wù)就應(yīng)該是可見(jiàn)的。

如果,db_trx_id>low_limit_id,則說(shuō)明,db_trx_id = 3的這個(gè)事務(wù)是在Read View中所有未提交的事務(wù)創(chuàng)建之后才提交的,也就是說(shuō),在當(dāng)前事務(wù)開(kāi)啟之后,有別的事務(wù)修改了數(shù)據(jù)并作了提交。所以,這個(gè)記錄對(duì)于當(dāng)前事務(wù)來(lái)說(shuō)應(yīng)該就是不可見(jiàn)的。(不可見(jiàn)怎么辦呢?后面講)

那么,還有另外一種情況,那就是up_limit_id > db_trx_id > low_limit_id,這種情況下,會(huì)再拿db_trx_id和Read View中的trx_ids進(jìn)行逐一比較。

如果,db_trx_id在trx_ids列表中,那么表示在當(dāng)前事務(wù)開(kāi)啟時(shí),并未提交的某個(gè)事務(wù)在修改數(shù)據(jù)之后提交了,那么這個(gè)記錄對(duì)于當(dāng)前事務(wù)來(lái)說(shuō)應(yīng)該是不可見(jiàn)的。

如果,db_trx_id不在trx_ids列表中,那么表示的是在當(dāng)前事務(wù)開(kāi)啟之前,其他事務(wù)對(duì)數(shù)據(jù)進(jìn)行修改并提交了,所有,這條記錄對(duì)當(dāng)前事務(wù)就應(yīng)該是可見(jiàn)的。

所以,當(dāng)讀取一條記錄的時(shí)候,經(jīng)過(guò)以上判斷,發(fā)現(xiàn)記錄對(duì)當(dāng)前事務(wù)可見(jiàn),那么就直接返回就行了。那么如果不可見(jiàn)怎么辦?沒(méi)錯(cuò),那就需要用到undo log了。

當(dāng)數(shù)據(jù)的事務(wù)ID不符合Read View規(guī)則時(shí)候,那就需要從undo log里面獲取數(shù)據(jù)的歷史快照,然后數(shù)據(jù)快照的事務(wù)ID再來(lái)和Read View進(jìn)行可見(jiàn)性比較,如果找到一條快照,則返回,找不到則返回空。

圖片

所以,總結(jié)一下,在InnoDB中,MVCC就是通過(guò)Read View + Undo Log來(lái)實(shí)現(xiàn)的,undo log中保存了歷史快照,而Read View 用來(lái)判斷具體哪一個(gè)快照是可見(jiàn)的。

MVCC和隔離級(jí)別

其實(shí),根據(jù)不同的事務(wù)隔離級(jí)別,Read View的獲取時(shí)機(jī)是不同的,在RC下,一個(gè)事務(wù)中的每一次SELECT都會(huì)重新獲取一次Read View,而在RR下,一個(gè)事務(wù)中只在第一次SELECT的時(shí)候會(huì)獲取一次Read View。

所以,可重復(fù)讀這種事務(wù)隔離級(jí)別之下,因?yàn)橛蠱VCC機(jī)制,就可以解決不可重復(fù)讀的問(wèn)題,因?yàn)樗挥性诘谝淮蜸ELECT的時(shí)候才會(huì)獲取一次Read View,天然不存在重復(fù)讀的問(wèn)題了。

參考資料:

  • https://dev.mysql.com/doc/refman/5.7/en/innodb-undo-logs.html
  • https://time.geekbang.org/column/article/120351
  • https://blog.csdn.net/SnailMann/article/details/94724197
  • http://www.scjtxx.cn/article/641019.html
  • https://zhuanlan.zhihu.com/p/52977862
責(zé)任編輯:武曉燕 來(lái)源: Hollis
相關(guān)推薦

2018-08-07 16:01:32

synchronizevolatilefinal

2018-07-03 14:54:25

Java內(nèi)存模型

2020-12-11 08:23:06

JavaMemory Mode內(nèi)存模型

2018-12-07 09:31:52

分布式鎖服務(wù)框架分布式系統(tǒng)

2022-11-08 09:33:36

訂單系統(tǒng)電商

2021-05-31 09:42:48

MySQL隔離級(jí)別

2021-02-22 13:32:19

MySQLSQL索引

2022-05-23 09:41:27

分庫(kù)分表數(shù)據(jù)庫(kù)算法

2019-11-05 14:06:07

MySQLB+索引

2019-11-04 15:00:50

MySQL索引B+樹(shù)

2021-03-08 10:25:37

MySQL數(shù)據(jù)庫(kù)索引

2021-03-08 12:47:42

MySQL查詢(xún)數(shù)據(jù)

2022-10-21 16:39:56

JDK優(yōu)化

2023-12-11 08:32:58

數(shù)據(jù)庫(kù)DruidDBA

2020-04-20 13:11:21

HashMap底層存儲(chǔ)

2020-04-28 09:15:58

HashMapJava數(shù)組

2019-09-19 14:03:32

B樹(shù)節(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)

2019-12-10 09:08:29

分布式開(kāi)源RocketMQ

2019-04-15 14:40:46

消息隊(duì)列Java編程

2020-12-10 13:46:35

人工智能
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)