0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

你是否對(duì)MySQL數(shù)據(jù)庫(kù)中的事務(wù)已經(jīng)有所了解呢?

jf_ro2CN3Fa ? 來(lái)源:頭條 ? 2023-02-21 17:20 ? 次閱讀

你是否對(duì) MySQL 數(shù)據(jù)庫(kù)中的事務(wù)已經(jīng)有所了解?看下面這張圖,按照 1~6 的順序依次執(zhí)行,在RR隔離級(jí)別下,事務(wù) A 和事務(wù) B 各自輸出的 num 值是多少嗎?

b321a948-a8d8-11ed-bfe3-dac502259ad0.png

我們預(yù)先創(chuàng)建好這樣一張表并初始化一條數(shù)據(jù):

CREATETABLE`test1`(
`id`int(11)NOTNULLAUTO_INCREMENTCOMMENT'主鍵Id',
`num`int(11)NULLCOMMENT'數(shù)量',
PRIMARYKEY(`id`)
)ENGINE=InnoDB;

insertintotest1(id,num)values(1,1);

然后開始按上圖的順序執(zhí)行各個(gè)事務(wù),這需要我們打開3個(gè)操作窗口來(lái)分別執(zhí)行 A、B、C 三個(gè)事務(wù):

事務(wù) A:

b33938ba-a8d8-11ed-bfe3-dac502259ad0.png

事務(wù) B:

b34dae80-a8d8-11ed-bfe3-dac502259ad0.png

事務(wù) C:

b36bd004-a8d8-11ed-bfe3-dac502259ad0.png

按照上圖的執(zhí)行順序執(zhí)行 commit,其中事務(wù) C 是自動(dòng)提交事務(wù)的,不需要我們顯示的 commit,事務(wù) A、B 的輸出結(jié)果如下:

事務(wù)A:num=1

事務(wù)B:num=3

為什么是這樣輸出?

它的背后其實(shí)是:MVCC(多版本并發(fā)控制)、consistent read(一致性讀)、locking reads(鎖定讀)等 MySQL 數(shù)據(jù)庫(kù)底層知識(shí)。

1、MVCC

MySQL 數(shù)據(jù)庫(kù)官網(wǎng)文檔是這樣來(lái)描述 MVCC 的:

b3863796-a8d8-11ed-bfe3-dac502259ad0.png

多版本控制: 指的是一種提高并發(fā)的技術(shù)。最早的數(shù)據(jù)庫(kù)系統(tǒng),只有讀讀之間可以并發(fā),讀寫,寫讀,寫寫都要阻塞。引入多版本之后,只有寫寫之間相互阻塞,其他三種操作都可以并行,這樣大幅度提高了 InnoDB 的并發(fā)度。在內(nèi)部實(shí)現(xiàn)中,與 Postgres 在數(shù)據(jù)行上實(shí)現(xiàn)多版本不同,InnoDB 是在 undolog 中實(shí)現(xiàn)的,通過 undolog 可以找回?cái)?shù)據(jù)的歷史版本。

找回的數(shù)據(jù)歷史版本可以提供給用戶讀(按照隔離級(jí)別的定義,有些讀請(qǐng)求只能看到比較老的數(shù)據(jù)版本),也可以在回滾的時(shí)候覆蓋數(shù)據(jù)頁(yè)上的數(shù)據(jù)。在 InnoDB 內(nèi)部中,會(huì)記錄一個(gè)全局的活躍讀寫事務(wù)數(shù)組,其主要用來(lái)判斷事務(wù)的可見性。

目前來(lái)看 MVCC 的實(shí)現(xiàn)依賴于:

隱藏字段(DB_TRX_ID、DB_ROLL_PTR)

回滾日志(undo log)

一致性讀(consistent read)

你也可以這樣去理解 MVCC:一個(gè)事務(wù)對(duì)數(shù)據(jù)進(jìn)行更新操作時(shí)候,先把舊的數(shù)據(jù)放到一個(gè)單獨(dú)的地方(回滾段),其他事務(wù)讀取數(shù)據(jù)時(shí)候,根據(jù) DB_TRX_ID、DB_ROLL_PTR 計(jì)算出 undo log 鏈中當(dāng)前版本的數(shù)據(jù)。

2、一致性讀(consistent read)

繼續(xù)看官方文檔對(duì) consistent read 的描述:

b3bab3f4-a8d8-11ed-bfe3-dac502259ad0.png

直譯:

一個(gè)讀操作使用基于某個(gè)時(shí)刻的快照信息來(lái)顯示查詢結(jié)果,而不考慮同時(shí)運(yùn)行的其他事務(wù)所執(zhí)行的更改。如果查詢到的數(shù)據(jù)被其他事務(wù)所更改,則根據(jù) undo log 中的內(nèi)容來(lái)重建原始數(shù)據(jù)。這種技術(shù)避免了一些通過強(qiáng)制事務(wù)等待其他事務(wù)完成而降低并發(fā)性的鎖定問題。

在 RR 級(jí)別下,首次讀操作被執(zhí)行時(shí)候創(chuàng)建一致性讀視圖 ReadView,事務(wù)的后續(xù)讀都基于該視圖的數(shù)據(jù);

在 RC 級(jí)別下,每一次讀操作都會(huì)創(chuàng)建一個(gè)最新的 ReadView,因此每次 select 讀都可以獲取到當(dāng)前已提交事務(wù)的最新數(shù)據(jù)。

“一致性讀”是 InnoDB 引擎在 RC 和 RR 隔離級(jí)別下處理 select 語(yǔ)句的默認(rèn)模式。因?yàn)橐粋€(gè)“一致性讀”是不需要對(duì)它訪問的表設(shè)置任何的鎖,當(dāng)對(duì)表執(zhí)行“一致性讀”時(shí)候,其他會(huì)話可以自由的修改這些表。

另外:

讀未提交(read uncommitted)、串行化(serializable)是不需要依賴 MVCC 的,讀未提交直接每次都讀取當(dāng)前數(shù)據(jù)的最新值即可。而 serializable 是直接采用加鎖的操作讓所有的事務(wù)都串行化執(zhí)行,犧牲了并發(fā)能力。

一致性讀的實(shí)現(xiàn)方式:

每個(gè)事務(wù)啟動(dòng)的瞬間,都會(huì)構(gòu)建一個(gè)數(shù)組(m_ids),用來(lái)記錄目前所有“活躍事務(wù)”(事務(wù)啟動(dòng)了,但是還沒提交)的 ID;

數(shù)組中的最小事務(wù) ID 為低水位;

數(shù)組中的最大事務(wù) ID + 1 為高水位;

數(shù)據(jù)版本可見性規(guī)則:當(dāng)前數(shù)據(jù)某個(gè)版本是否可見,取決于當(dāng)前數(shù)據(jù)的 DB_TRX_ID 以及這個(gè)一致性視圖數(shù)組中記錄的事務(wù) ID 做對(duì)比來(lái)判斷:低水位以前的數(shù)據(jù)版本可見,高水位以后的數(shù)據(jù)版本不可見,低水位和高水位之間得查看當(dāng)前數(shù)據(jù)版本的 DB_TRX_ID 是否存在數(shù)組中,若存在意味著事務(wù)未提交,不可見,若不存在意味著事務(wù)已提交,可見。

b3fa8894-a8d8-11ed-bfe3-dac502259ad0.png

那按照一致性讀的理解,事務(wù)B已經(jīng)創(chuàng)建了自己的快照數(shù)據(jù)了,它的輸出應(yīng)該是 num = 2 呀,為什么會(huì)是 num=3?

可是如果不是 num=3,那么已經(jīng)提交的事務(wù) C 的操作不就丟失了嗎?(產(chǎn)生丟失更新問題)

這里又涉及到一個(gè)知識(shí)點(diǎn):

更新數(shù)據(jù)都是先讀后寫的,而這個(gè)讀,只能讀當(dāng)前的值,稱為“當(dāng)前讀”(current read)。

3、當(dāng)前讀(current reads)

也叫做鎖定讀(locking reads)

b40abc00-a8d8-11ed-bfe3-dac502259ad0.png

InnoDB 引擎支持兩種方式的鎖定讀以提供額外的安全性(MySQL 5.7 版本):

#讀鎖(S鎖,共享鎖)
SELECT...LOCKINSHAREMODE;
#寫鎖(X鎖,排他鎖)
SELECT...FORUPDATE;

鎖定讀會(huì)在被讀取的數(shù)據(jù)上加一把共享鎖,其他事務(wù)可以讀取記錄,但是不可以修改記錄,直到當(dāng)前事務(wù)提交。

鎖定讀驗(yàn)證:

b4225c70-a8d8-11ed-bfe3-dac502259ad0.png

為什么要有鎖定讀?

如果你在一個(gè)事務(wù)中先查詢了一個(gè)數(shù)據(jù),然后插入或者更新相關(guān)的數(shù)據(jù),這個(gè)時(shí)候來(lái)了一個(gè)事務(wù)B同時(shí)更新或者刪除你要查詢的記錄,就會(huì)出現(xiàn)幻讀問題了。

這也是為什么 MVCC 不能完全解決幻讀的問題,而是需要 MVCC + 行鎖 + 間隙鎖(next-key lock)的方式。

4、事務(wù) A、B、C 的執(zhí)行流程

繼續(xù)看開頭的第一張圖:

b321a948-a8d8-11ed-bfe3-dac502259ad0.png

starttransactionwithconsistentsnapshot;

這條 SQL 語(yǔ)句可以立即啟動(dòng)事務(wù),創(chuàng)建當(dāng)前事務(wù)的一致性讀快照。效果等同于 start transaction 然后馬上執(zhí)行 select 語(yǔ)句。

我們接下來(lái)看看文章開頭的三個(gè)事務(wù)對(duì)數(shù)據(jù)行的修改流程,按照步驟 1~6 的操作如下:

b4551890-a8d8-11ed-bfe3-dac502259ad0.png

如果大家細(xì)致的查看上圖的三個(gè)事務(wù)的穿插執(zhí)行流程,可以發(fā)現(xiàn),A、B、C 三個(gè)事務(wù)無(wú)論是 commit 還是 rollback,都是可以最終得到正確的數(shù)據(jù)。

這就是 InnoDB 引擎下的多版本并發(fā)控制(MVCC)的實(shí)現(xiàn)原理。

總結(jié)以下幾個(gè)關(guān)鍵點(diǎn):

每一個(gè)事務(wù)都會(huì)創(chuàng)建一個(gè)數(shù)據(jù)快照,快照創(chuàng)建的時(shí)機(jī)根據(jù)隔離級(jí)別的不同有所區(qū)別;

每一個(gè)事務(wù)都會(huì)生成一個(gè)全局唯一的 DB_TRX_ID,用于標(biāo)記當(dāng)前版本;

DB_ROLL_PTR 是回滾指針的意思,結(jié)合 DB_TRX_ID 來(lái)最終確定我要拿到的數(shù)據(jù);

DB_TRX_ID、DB_ROLL_PTR、undo log 這三個(gè)值來(lái)控制數(shù)據(jù)的版本;

update、delete 操作都是先讀后寫,這個(gè)讀屬于鎖定讀(當(dāng)前讀)。






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • TRX
    TRX
    +關(guān)注

    關(guān)注

    0

    文章

    3

    瀏覽量

    9217
  • MySQL
    +關(guān)注

    關(guān)注

    1

    文章

    811

    瀏覽量

    26580
  • MYSQL數(shù)據(jù)庫(kù)

    關(guān)注

    0

    文章

    96

    瀏覽量

    9392
  • MVCC
    +關(guān)注

    關(guān)注

    0

    文章

    13

    瀏覽量

    1470

原文標(biāo)題:MySQL 底層之 MVCC、回滾段、一致性讀、鎖定讀

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    如何在Rust連接和使用MySQL數(shù)據(jù)庫(kù)

    如何在Rust連接和使用MySQL數(shù)據(jù)庫(kù)。 安裝 mysql 模塊 這里我們假設(shè)已經(jīng)安裝了R
    的頭像 發(fā)表于 09-30 17:05 ?1678次閱讀

    labview連接mysql數(shù)據(jù)庫(kù)的問題

    這個(gè)ODBC數(shù)據(jù)已經(jīng)成功設(shè)置了,為什么用labview連接mysql數(shù)據(jù)庫(kù)就出現(xiàn)這個(gè)錯(cuò)誤?望大神解答,謝謝啦!
    發(fā)表于 08-19 08:30

    詳解Mysql數(shù)據(jù)庫(kù)InnoDB存儲(chǔ)引擎事務(wù)

    關(guān)于Mysql數(shù)據(jù)庫(kù)InnoDB存儲(chǔ)引擎事務(wù)的一點(diǎn)理解
    發(fā)表于 05-13 10:11

    什么是MySQL數(shù)據(jù)庫(kù)?ASPNET和MySQL數(shù)據(jù)庫(kù)的入門簡(jiǎn)介

    在ASP時(shí)代,如果我們要建立一個(gè)數(shù)據(jù)庫(kù)驅(qū)動(dòng)的web站點(diǎn),那么可以選擇環(huán)很多錢的微軟SQL SERVER數(shù)據(jù)庫(kù)或者選擇要花很多時(shí)間來(lái)尋找達(dá)到性能和穩(wěn)定性統(tǒng)一的ACCESS數(shù)據(jù)庫(kù),但在.
    發(fā)表于 11-22 16:20 ?4次下載

    MySQL數(shù)據(jù)庫(kù)如何安裝和使用說明

    MySQL數(shù)據(jù)庫(kù)開發(fā) 基礎(chǔ)概念 1.數(shù)據(jù):描述事物特征的符號(hào),屬性 2.數(shù)據(jù)庫(kù)的概念:管理計(jì)算機(jī)
    的頭像 發(fā)表于 02-13 16:13 ?2801次閱讀

    Mysql和Oracle數(shù)據(jù)庫(kù)之間的誤區(qū)

    Mysql 和Oracle 在開發(fā)的使用是隨處可見的,那就簡(jiǎn)單去了解一下這倆款火的不行的數(shù)據(jù)庫(kù)。 本質(zhì)區(qū)別: Oracle數(shù)據(jù)庫(kù)是一個(gè)對(duì)象
    的頭像 發(fā)表于 11-13 09:35 ?1914次閱讀
    <b class='flag-5'>Mysql</b>和Oracle<b class='flag-5'>數(shù)據(jù)庫(kù)</b>之間的誤區(qū)

    華為云數(shù)據(jù)庫(kù)-RDS for MySQL數(shù)據(jù)庫(kù)

    (for MySQL)為輔。 MySQL數(shù)據(jù)庫(kù)是全球最受歡迎的一種數(shù)據(jù)庫(kù),它是屬于 Oracle旗下的一款產(chǎn)品,MySQL是一種關(guān)系型
    的頭像 發(fā)表于 10-27 11:06 ?1526次閱讀

    有哪些不同的MySQL數(shù)據(jù)庫(kù)引擎?

    數(shù)據(jù)庫(kù)引擎是MySQL組件,可以處理SQL操作,例如從數(shù)據(jù)庫(kù)創(chuàng)建、讀取和更新數(shù)據(jù)。MySQL中有兩種類型的引擎:
    的頭像 發(fā)表于 04-03 16:38 ?1142次閱讀

    MySQL數(shù)據(jù)庫(kù)管理與應(yīng)用

    討論MySQL數(shù)據(jù)庫(kù)的管理和應(yīng)用。 管理MySQL數(shù)據(jù)庫(kù) 在管理MySQL數(shù)據(jù)庫(kù)之前,我們需要
    的頭像 發(fā)表于 08-28 17:15 ?977次閱讀

    數(shù)據(jù)庫(kù)mysql基本增刪改查

    的基本增刪改查操作。 一、增加數(shù)據(jù)(INSERT) 在MySQL,可以使用INSERT語(yǔ)句來(lái)向數(shù)據(jù)庫(kù)添加
    的頭像 發(fā)表于 11-16 16:35 ?1517次閱讀

    MySQL數(shù)據(jù)庫(kù)基礎(chǔ)知識(shí)

    的基礎(chǔ)知識(shí),包括其架構(gòu)、數(shù)據(jù)類型、表操作、查詢語(yǔ)句和數(shù)據(jù)導(dǎo)入導(dǎo)出等方面。 MySQL 數(shù)據(jù)庫(kù)架構(gòu) MySQL
    的頭像 發(fā)表于 11-21 11:09 ?973次閱讀

    mysql數(shù)據(jù)庫(kù)基礎(chǔ)命令

    MySQL是一個(gè)流行的關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng),經(jīng)常用于存儲(chǔ)、管理和操作數(shù)據(jù)。在本文中,我們將詳細(xì)介紹MySQL的基礎(chǔ)命令,并提供與每個(gè)命令相關(guān)的詳細(xì)解釋。 登錄
    的頭像 發(fā)表于 12-06 10:56 ?591次閱讀

    eclipse怎么連接數(shù)據(jù)庫(kù)mysql

    連接Eclipse和MySQL數(shù)據(jù)庫(kù)可以通過JDBC(Java Database Connectivity)來(lái)實(shí)現(xiàn)。以下是詳細(xì)步驟: 下載并安裝MySQL數(shù)據(jù)庫(kù)
    的頭像 發(fā)表于 12-06 11:06 ?1312次閱讀

    mysql怎么新建一個(gè)數(shù)據(jù)庫(kù)

    mysql怎么新建一個(gè)數(shù)據(jù)庫(kù) 如何新建一個(gè)數(shù)據(jù)庫(kù)MySQL 創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)
    的頭像 發(fā)表于 12-28 10:01 ?902次閱讀

    數(shù)據(jù)庫(kù)數(shù)據(jù)恢復(fù)—Mysql數(shù)據(jù)庫(kù)表記錄丟失的數(shù)據(jù)恢復(fù)流程

    Mysql數(shù)據(jù)庫(kù)故障: Mysql數(shù)據(jù)庫(kù)表記錄丟失。 Mysql數(shù)據(jù)庫(kù)故障表現(xiàn): 1、
    的頭像 發(fā)表于 12-16 11:05 ?160次閱讀
    <b class='flag-5'>數(shù)據(jù)庫(kù)</b><b class='flag-5'>數(shù)據(jù)</b>恢復(fù)—<b class='flag-5'>Mysql</b><b class='flag-5'>數(shù)據(jù)庫(kù)</b>表記錄丟失的<b class='flag-5'>數(shù)據(jù)</b>恢復(fù)流程