偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

通俗易懂的MySQL事務(wù)及MVCC原理

數(shù)據(jù)庫(kù) MySQL
事務(wù)指的是一組命令操作,在執(zhí)行的過(guò)程中,要么全部成功,要么全部失敗。由引擎層支持事務(wù),MyISAM就不支持事務(wù),而InnoDB是支持事務(wù)的。

[[433197]]

一、事務(wù)簡(jiǎn)介與四大特性

事務(wù)指的是一組命令操作,在執(zhí)行的過(guò)程中,要么全部成功,要么全部失敗。

由引擎層支持事務(wù),MyISAM就不支持事務(wù),而InnoDB是支持事務(wù)的。

事務(wù)具有以下四大特性(ACID):

  • 原子性(Atomicity):指事務(wù)不可分割,要么全部成功,要么全部失敗,不可能存在部分成功或部分失敗的情況。如果執(zhí)行某一條語(yǔ)句失敗后,將會(huì)觸發(fā)之前所有執(zhí)行過(guò)的語(yǔ)句的回滾,因此靠的是undo log。
  • 一致性(Consistency):在事務(wù)執(zhí)行前后,數(shù)據(jù)的完整性沒(méi)有遭到破壞。一致性是mysql追求的最終目標(biāo),需要數(shù)據(jù)庫(kù)層面與應(yīng)用層面同時(shí)來(lái)維護(hù)。需要先滿(mǎn)足原子性、隔離性與持久性,同時(shí)也需要應(yīng)用層面做保障,即在應(yīng)用層面對(duì)數(shù)據(jù)進(jìn)行檢驗(yàn)。
  • 隔離性(Isolation):事務(wù)之前是隔離的,并發(fā)執(zhí)行的事務(wù)之間不存在互相影響,mysql通過(guò)鎖以及MVCC來(lái)保證隔離性。
  • 持久性(Durability):事務(wù)一旦提交,那么對(duì)數(shù)據(jù)的操作就是永久性的,即使接下來(lái)數(shù)據(jù)庫(kù)宕機(jī)也不會(huì)有影響。mysql是通過(guò)redo log來(lái)實(shí)現(xiàn)宕機(jī)恢復(fù)的,而binlog主要是用來(lái)誤刪恢復(fù)與主從復(fù)制的。

簡(jiǎn)單羅列了一下4種特性以及對(duì)應(yīng)的實(shí)現(xiàn)方式,有關(guān)ACID詳細(xì)的實(shí)現(xiàn)原理,會(huì)另開(kāi)篇幅!

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

當(dāng)事務(wù)存在并發(fā)時(shí),就會(huì)產(chǎn)生以下問(wèn)題。

臟讀

即讀取到別的事務(wù)未提交的數(shù)據(jù)。

A事務(wù)讀取B事務(wù)尚未提交的數(shù)據(jù),此時(shí)如果B事務(wù)發(fā)生錯(cuò)誤并執(zhí)行回滾操作,那么A事務(wù)讀取到的數(shù)據(jù)就是臟數(shù)據(jù)。

就好像原本的數(shù)據(jù)比較干凈、純粹,此時(shí)由于B事務(wù)更改了它,這個(gè)數(shù)據(jù)變得不再純粹。

這個(gè)時(shí)候A事務(wù)立即讀取了這個(gè)臟數(shù)據(jù),但事務(wù)B良心發(fā)現(xiàn),又用回滾把數(shù)據(jù)恢復(fù)成原來(lái)干凈、純粹的樣子,而事務(wù)A卻什么都不知道,最終結(jié)果就是事務(wù)A讀取了此次的臟數(shù)據(jù),稱(chēng)為臟讀。

這種情況常發(fā)生于轉(zhuǎn)賬與取款操作中

不可重復(fù)讀

即某個(gè)事務(wù)前后多次讀取,數(shù)據(jù)內(nèi)容不一致。

事務(wù)A在執(zhí)行讀取操作,由整個(gè)事務(wù)A比較大,前后讀取同一條數(shù)據(jù)需要經(jīng)歷很長(zhǎng)的時(shí)間 。

而在事務(wù)A第一次讀取數(shù)據(jù),比如此時(shí)讀取了小明的年齡為20歲,事務(wù)B執(zhí)行更改操作,將小明的年齡更改為30歲,此時(shí)事務(wù)A第二次讀取到小明的年齡時(shí),發(fā)現(xiàn)其年齡是30歲,和之前的數(shù)據(jù)不一樣了,也就是數(shù)據(jù)不重復(fù)了,系統(tǒng)不可以讀取到重復(fù)的數(shù)據(jù),成為不可重復(fù)讀。

幻讀

即某個(gè)事務(wù)前后多次讀取,讀到的數(shù)據(jù)總量不一致。

事務(wù)A在執(zhí)行讀取操作,需要兩次統(tǒng)計(jì)數(shù)據(jù)的總量,前一次查詢(xún)數(shù)據(jù)總量后,此時(shí)事務(wù)B執(zhí)行了新增數(shù)據(jù)的操作并提交后,這個(gè)時(shí)候事務(wù)A讀取的數(shù)據(jù)總量和之前統(tǒng)計(jì)的不一樣,就像產(chǎn)生了幻覺(jué)一樣,平白無(wú)故的多了幾條數(shù)據(jù),稱(chēng)為幻讀。

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

事務(wù)隔離級(jí)別,就是在不同程度上解決以上的問(wèn)題。

有四種隔離級(jí)別,分別是

  • 讀未提交(Read Uncommitted)
  • 讀已提交(Read Committed)
  • 可重復(fù)讀(Repeatable Read)
  • 串行化(Serializable)

讀未提交

在這種隔離級(jí)別下,所有事務(wù)能夠讀取其他事務(wù)未提交的數(shù)據(jù)。

讀取其他事務(wù)未提交的數(shù)據(jù),會(huì)造成臟讀。因此在該種隔離級(jí)別下,不能解決臟讀、不可重復(fù)讀和幻讀。

讀未提交可能會(huì)產(chǎn)生臟讀的現(xiàn)象,那么怎么解決臟讀呢?那就是使用讀已提交。

讀已提交

在這種隔離級(jí)別下,所有事務(wù)只能讀取其他事務(wù)已經(jīng)提交的內(nèi)容。

能夠徹底解決臟讀的現(xiàn)象。但在這種隔離級(jí)別下,會(huì)出現(xiàn)一個(gè)事務(wù)的前后多次的查詢(xún)中卻返回了不同內(nèi)容的數(shù)據(jù)的現(xiàn)象,也就是出現(xiàn)了不可重復(fù)讀。

這是大多數(shù)數(shù)據(jù)庫(kù)系統(tǒng)默認(rèn)的隔離級(jí)別,例如Oracle和SQL Server,但mysql不是。

已提交可能會(huì)產(chǎn)生不可重復(fù)讀的現(xiàn)象,我們可以使用可重復(fù)讀。

可重復(fù)讀

在這種隔離級(jí)別下,所有事務(wù)前后多次的讀取到的數(shù)據(jù)內(nèi)容是不變的。

也就是某個(gè)事務(wù)在執(zhí)行的過(guò)程中,不允許其他事務(wù)進(jìn)行update操作,但允許其他事務(wù)進(jìn)行add操作,造成某個(gè)事務(wù)前后多次讀取到的數(shù)據(jù)總量不一致的現(xiàn)象,從而產(chǎn)生幻讀。

這才是mysql的默認(rèn)事務(wù)隔離級(jí)別

可重復(fù)讀依然會(huì)產(chǎn)生幻讀的現(xiàn)象,此時(shí)我們可以使用串行化來(lái)解決。

串行化

在這種隔離級(jí)別下,所有的事務(wù)順序執(zhí)行,所以他們之間不存在沖突,從而能有效地解決臟讀、不可重復(fù)讀和幻讀的現(xiàn)象。

但是安全和效率不能兼得,串行化會(huì)大大降低數(shù)據(jù)庫(kù)的性能,一般不使用這種級(jí)別。

下面用一張表格來(lái)表示他們能夠解決的問(wèn)題,x代表未解決,√代表能夠解決。

 

當(dāng)然,以上所說(shuō)的隔離級(jí)別及當(dāng)前級(jí)別存在的問(wèn)題只是一種規(guī)范,不同的數(shù)據(jù)庫(kù)廠商可以有不同的實(shí)現(xiàn)。

例如在mysql的可重復(fù)讀的級(jí)別上,使用臨鍵鎖的方式就已經(jīng)解決了幻讀的問(wèn)題。

四、MVCC

mysql為了實(shí)現(xiàn)以上隔離級(jí)別,提出了LBCC(Lock-Based Concurrent Control,基于鎖的并發(fā)控制)與MVCC(Multi-Version Concurrent Control,基于多版本的并發(fā)控制)。

在LBCC中,讀寫(xiě)沖突,會(huì)使用諸如記錄鎖、間隙鎖與臨鍵鎖等鎖來(lái)實(shí)現(xiàn)數(shù)據(jù)的并發(fā)安全,因此讀寫(xiě)性能不高。關(guān)于鎖的分類(lèi),可以參考我的另外一篇文章談?wù)勬i的類(lèi)型

在MVCC中,讀寫(xiě)不沖突,記錄每一行的多個(gè)版本,來(lái)避免在多個(gè)事務(wù)之間的競(jìng)爭(zhēng)。以空間換時(shí)間的思路,極大地提高了讀寫(xiě)性能。

[[433198]]

MVCC主要靠undo log版本鏈與ReadView來(lái)實(shí)現(xiàn)。

先對(duì)undo log有一個(gè)基本的認(rèn)識(shí)

Undo log

undo log主要用于事務(wù)回滾時(shí)恢復(fù)原來(lái)的數(shù)據(jù)

mysql在執(zhí)行sql語(yǔ)句時(shí),會(huì)將一條邏輯相反的日志保存到undo log中。因此,undo log中記錄的也是邏輯日志。

當(dāng)sql語(yǔ)句為insert時(shí),會(huì)在undo log中記錄本次插入的主鍵id。等事務(wù)回滾時(shí),delete此id即可。

當(dāng)sql語(yǔ)句為update時(shí),會(huì)在undo log中記錄修改前的數(shù)據(jù)。等事務(wù)回滾時(shí),再執(zhí)行一次update,得到原來(lái)的數(shù)據(jù)。

當(dāng)sql語(yǔ)句為delete時(shí),會(huì)在undo log中記錄刪除前的數(shù)據(jù)。等事務(wù)回滾時(shí),insert原來(lái)的數(shù)據(jù)即可。

數(shù)據(jù)庫(kù)事務(wù)四大特性中的原子性,即事務(wù)具有不可分割性,要么全部成功,要么全部失敗,其底層就靠undo log實(shí)現(xiàn)。在某一步執(zhí)行失敗時(shí),會(huì)對(duì)之前事務(wù)的語(yǔ)句進(jìn)行回滾。

對(duì)數(shù)據(jù)庫(kù)中的日志完全不熟悉的話(huà),可以看我的另外一篇文章數(shù)據(jù)庫(kù)日志——binlog、redo log、undo log掃盲

行的隱藏列

在數(shù)據(jù)庫(kù)中的每一行上,除了存放真實(shí)的數(shù)據(jù)以外,還存在著3個(gè)隱藏列——row_id、trx_id與roll_pointer。

row_id,行號(hào)

如果當(dāng)前表有整數(shù)類(lèi)型的主鍵,則row_id就是主鍵的值。

如果沒(méi)有整數(shù)類(lèi)型的主鍵,則mysql會(huì)按照字段順序選擇一個(gè)非空的整數(shù)類(lèi)型的唯一索引作為row_id。

如果mysql沒(méi)有找到,則會(huì)自動(dòng)生成一個(gè)自動(dòng)增長(zhǎng)的整數(shù)作為row_id。

那row_id和今天的MVCC有什么關(guān)系呢?

不能說(shuō)一點(diǎn)沒(méi)有吧,只能說(shuō)毫無(wú)關(guān)系。

trx_id,事務(wù)號(hào)

當(dāng)一個(gè)事務(wù)開(kāi)始執(zhí)前,mysql會(huì)為這個(gè)事務(wù)分配一個(gè)全局自增的事務(wù)id。

之后該事務(wù)對(duì)當(dāng)前行進(jìn)行的增、刪、改操作時(shí),都會(huì)將自己的事務(wù)id記錄到trx_id中。

roll_pointer,回滾指針

事務(wù)對(duì)當(dāng)前行進(jìn)行改動(dòng)時(shí),會(huì)將舊數(shù)據(jù)寫(xiě)入進(jìn)undo log中,再將新數(shù)據(jù)寫(xiě)入當(dāng)前行,且當(dāng)前行的roll_pointer指向剛才那個(gè)undo log,因此可以通過(guò)roll_pointer找到該行的前一個(gè)版本。

當(dāng)一直有事務(wù)對(duì)該行改動(dòng)時(shí),就會(huì)一直生成undo log,最終將會(huì)形成undo log版本鏈。

Undo log版本鏈

一開(kāi)始,我們使用以下語(yǔ)句創(chuàng)建一個(gè)stduent表

  1. CREATE TABLE `student` ( 
  2.     `id` INT ( 11 ) NOT NULL AUTO_INCREMENT, 
  3.     `nameVARCHAR ( 255 ) NOT NULL
  4.     `age` INT ( 11 ) NOT NULL
  5.   PRIMARY KEY ( `id` ) USING BTREE  
  6. ) ENGINE = INNODB; 

現(xiàn)在開(kāi)啟第1個(gè)事務(wù),事務(wù)id為1,執(zhí)行以下插入語(yǔ)句。

  1. INSERT INTO student VALUES ( 1, "a", 24 ); 

那么當(dāng)前行的一個(gè)示意圖如下:

因?yàn)樵摂?shù)據(jù)是新插入的,因此它的roll_pointer指向的undo log為空。

接著開(kāi)啟第2個(gè)事務(wù),分配的事務(wù)id是2,執(zhí)行以下修改命令。

  1. UPDATE student SET NAME = 'b' WHERE id = 1; 

現(xiàn)在的示意圖變?yōu)椋?/p>

當(dāng)開(kāi)啟第3個(gè)事務(wù),分配到事務(wù)id是3,執(zhí)行以下修改命令。

  1. UPDATE student SET age = 25 WHERE id = 1; 

示意圖變?yōu)椋?/p>

每一個(gè)事務(wù)對(duì)該行改動(dòng)時(shí),都會(huì)生成一個(gè)undo log,用于保存之前的版本,之后再將新版本的roll_pointer指向剛才生成的undo log。

因此roll_pointer可以將這些不同版本的undo log串聯(lián)起來(lái),形成undo log版本鏈。

ReadView

首先需要理解一下快照讀與當(dāng)前讀

快照讀:簡(jiǎn)單的select查詢(xún),即不包括 select ... lock in share mode, select ... for update,可能會(huì)讀到數(shù)據(jù)的歷史版本。

當(dāng)前讀:以下語(yǔ)句都是當(dāng)前讀,總是讀取最新版本,會(huì)對(duì)讀取的最新版本加鎖。

  • select ... lock in share mode
  • select ... for update
  • insert
  • update
  • delete

在事務(wù)執(zhí)行每一個(gè)快照讀或事務(wù)初次執(zhí)行快照讀時(shí),會(huì)生成一致性視圖,即ReadView。

ReadView的作用是,判斷undo log版本鏈中的哪些數(shù)據(jù)對(duì)當(dāng)前事務(wù)可見(jiàn)。

ReadView包含以下幾個(gè)重要的參數(shù):

m_ids

在創(chuàng)建ReadView的那一刻,mysql中所有未提交的事務(wù)id集合。

min_trx_id

m_ids中的最小值

max_trx_id

mysql即將為下一個(gè)事務(wù)分配的事務(wù)id,并不是m_ids中的最大值。

creator_trx_id

即創(chuàng)建此ReadView的事務(wù)id

簡(jiǎn)要的示意圖如下:

那么事務(wù)在執(zhí)行快照讀時(shí),可以通過(guò)以下的規(guī)則來(lái)確定undo log版本鏈上的哪個(gè)版本數(shù)據(jù)可見(jiàn)。

如果當(dāng)前undo log的版本的trx_id

如果當(dāng)前undo log的版本的trx_id≥max_trx_id,說(shuō)明該版本對(duì)應(yīng)的事務(wù)在生成ReadView之后才開(kāi)始的,因此是不可見(jiàn)的。

如果當(dāng)前undo log的版本的trx_id∈[min_trx_id,max_trx_id),如果在這個(gè)范圍里,還要判斷trx_id是否在m_ids中:

在m_ids中,說(shuō)明版本對(duì)應(yīng)的事務(wù)未提交,因此是不可見(jiàn)的。

不在m_ids中,說(shuō)明版本對(duì)應(yīng)的事務(wù)已經(jīng)提交,因此是可見(jiàn)的。

如果當(dāng)前undo log的版本的trx_id=creator_trxt_id,說(shuō)明事務(wù)正在訪問(wèn)自己修改的數(shù)據(jù),因此是可見(jiàn)的。

當(dāng)undo log版本鏈表的頭結(jié)點(diǎn)數(shù)據(jù)被判定為不可見(jiàn)時(shí),則利用roll_pointer找到上一個(gè)版本,再進(jìn)行判斷。如果整個(gè)鏈表中都沒(méi)有找到可見(jiàn)的數(shù)據(jù),則代表當(dāng)前的查詢(xún)找不到數(shù)據(jù)。

[[433201]]

MVCC在四種隔離級(jí)別下的區(qū)別

在Read Uncommitted級(jí)別下,事務(wù)總是讀取到最新的數(shù)據(jù),因此根本用不到歷史版本,所以MVCC不在該級(jí)別下工作。

在Serializable級(jí)別下,事務(wù)總是順序執(zhí)行。寫(xiě)會(huì)加寫(xiě)鎖,讀會(huì)加讀鎖,完全用不到MVCC,所以MVCC也不在該級(jí)別下工作。

真正和MVCC兼容的隔離級(jí)別是Read Committed(RC)與Repeatable Read(RR)

MVCC在RC與RR級(jí)別下的區(qū)別,在于生成ReadView的頻率不同。

在RC級(jí)別下,當(dāng)前事務(wù)總是希望讀取到別的事務(wù)已經(jīng)提交的數(shù)據(jù),因此當(dāng)前事務(wù)事務(wù)會(huì)在執(zhí)行每一次快照讀的情況下都會(huì)去生成ReadView,實(shí)時(shí)更新m_ids,及時(shí)發(fā)現(xiàn)那些已經(jīng)提交的事務(wù)。

在RR級(jí)別下,當(dāng)前事務(wù)當(dāng)然也能夠讀取到別的事務(wù)已經(jīng)提交的數(shù)據(jù),但為了避免不可重復(fù)讀,因此只會(huì)在執(zhí)行第一次快照讀的情況下去生成ReadView,之后的快照讀會(huì)一直沿用該ReadView。

舉個(gè)栗子:

在RC級(jí)別下

一開(kāi)始,事務(wù)id為1的事務(wù)往表里插入了一條數(shù)據(jù),版本鏈如下:

這個(gè)時(shí)候,開(kāi)啟事務(wù)id為2的事務(wù),關(guān)閉自動(dòng)提交模式。先執(zhí)行一次select *查詢(xún),生成的ReadView如下

  1. m_ids={2},min_trx_id=2,max_trx_id=3,creator_trx_id=2 

由于該條數(shù)據(jù)的trx_id

因此,事務(wù)2能直接查到該數(shù)據(jù)。

現(xiàn)在開(kāi)啟事務(wù)3,事務(wù)id為3,將該條數(shù)據(jù)的name改為b,并自動(dòng)提交,版本鏈如下:

這個(gè)時(shí)候,事務(wù)2再次select *查詢(xún),由于處于RC級(jí)別下,會(huì)再次生成ReadView,此時(shí)的ReadView如下:

  1. m_ids={2},min_trx_id=2,max_trx_id=4,creator_trx_id=2 

由于最新版本的trx_id∈[2,4)且trx_id不在m_ids中,說(shuō)明該版本的數(shù)據(jù)已經(jīng)提交,因此是可見(jiàn)的,所以事務(wù)2能查到最新的數(shù)據(jù)。

而處于RR級(jí)別下:

事務(wù)2再次select *查詢(xún)時(shí),不會(huì)生成ReadView,而是沿用第一次生成的ReadView:

  1. m_ids={2},min_trx_id=2,max_trx_id=3,creator_trx_id=2 

由于最新版本的trx_id≥max_trx_id,說(shuō)明該版本對(duì)應(yīng)的事務(wù)在生成ReadView之后才開(kāi)始的,因此是不可見(jiàn)的。

所以沿著roll_pointer找到上一個(gè)版本,上一個(gè)版本的trx_id

所以,事務(wù)2只能查詢(xún)到舊版本的數(shù)據(jù),兩次的查詢(xún)一致,避免了不可重復(fù)讀。

 

責(zé)任編輯:姜華 來(lái)源: 今日頭條
相關(guān)推薦

2011-10-26 19:57:33

2019-06-19 08:30:47

網(wǎng)絡(luò)協(xié)議IPTCP

2009-12-31 10:59:22

ADSL技術(shù)原理

2023-01-06 09:40:20

項(xiàng)目性能

2020-06-08 10:50:58

前端TypeScript代碼

2021-05-26 16:12:20

區(qū)塊鏈加密貨幣比特幣

2022-06-28 07:31:11

哨兵模式redis

2022-09-23 08:32:53

微服務(wù)架構(gòu)服務(wù)

2022-07-06 08:17:50

C 語(yǔ)言函數(shù)選型

2019-05-20 07:37:00

TCPIP網(wǎng)絡(luò)協(xié)議

2018-01-17 22:36:46

區(qū)塊鏈數(shù)字貨幣比特幣

2021-05-30 19:02:59

變量對(duì)象上下文

2021-05-25 09:50:01

GitLinux命令

2018-03-11 15:11:38

物聯(lián)網(wǎng)數(shù)據(jù)物聯(lián)網(wǎng)數(shù)據(jù)

2018-03-05 08:35:12

物聯(lián)網(wǎng)互聯(lián)網(wǎng)網(wǎng)絡(luò)技術(shù)

2021-05-13 13:20:00

Git命令Linux

2018-03-29 06:40:26

物聯(lián)網(wǎng)

2018-03-11 14:57:07

物聯(lián)網(wǎng)組網(wǎng)無(wú)線通信

2023-08-03 16:02:24

Objectwaitnotify

2021-12-26 15:19:39

HTTPS網(wǎng)絡(luò)協(xié)議網(wǎng)絡(luò)傳輸
點(diǎn)贊
收藏

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