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

日常學(xué)習(xí)MySQL底層之MVCC、回滾段、一致性讀、鎖定讀

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

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

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

  1. CREATE TABLE `test1`  ( 
  2.   `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵Id'
  3.   `num` int(11) NULL COMMENT '數(shù)量'
  4.   PRIMARY KEY (`id`) 
  5. ) ENGINE = InnoDB; 
  6. insert into test1(id, num) values (1, 1); 

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

事務(wù)A:

 

事務(wù)A

 

事務(wù)B:

 

事務(wù)B

 

事務(wù)C:

 

事務(wù)C

 

按照上圖的執(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的:

官網(wǎng)鏈接:https://dev.mysql.com/doc/refman/8.0/en/innodb-multi-versioning.html

淘寶的數(shù)據(jù)庫(kù)內(nèi)核月報(bào)中有提到:

  • 多版本控制: 指的是一種提高并發(fā)的技術(shù)。最早的數(shù)據(jù)庫(kù)系統(tǒng),只有讀讀之間可以并發(fā),讀寫(xiě),寫(xiě)讀,寫(xiě)寫(xiě)都要阻塞。引入多版本之后,只有寫(xiě)寫(xiě)之間相互阻塞,其他三種操作都可以并行,這樣大幅度提高了InnoDB的并發(fā)度。在內(nèi)部實(shí)現(xiàn)中,與Postgres在數(shù)據(jù)行上實(shí)現(xiàn)多版本不同,InnoDB是在undolog中實(shí)現(xiàn)的,通過(guò)undolog可以找回?cái)?shù)據(jù)的歷史版本。找回的數(shù)據(jù)歷史版本可以提供給用戶讀(按照隔離級(jí)別的定義,有些讀請(qǐng)求只能看到比較老的數(shù)據(jù)版本),也可以在回滾的時(shí)候覆蓋數(shù)據(jù)頁(yè)上的數(shù)據(jù)。在InnoDB內(nèi)部中,會(huì)記錄一個(gè)全局的活躍讀寫(xiě)事務(wù)數(shù)組,其主要用來(lái)判斷事務(wù)的可見(jiàn)性。

目前來(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的描述:

 

官網(wǎng)鏈接:https://dev.mysql.com/doc/refman/8.0/en/glossary.html#glos_consistent_read

直譯:

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

在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ì)它訪問(wèn)的表設(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)了,但是還沒(méi)提交)的 ID;
  • 數(shù)組中的最小事務(wù)ID為低水位;
  • 數(shù)組中的最大事務(wù)ID+1為高水位;

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

 

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

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

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

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

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

也叫做鎖定讀(locking reads)

官方文檔:https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html

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

  1. # 讀鎖(S 鎖,共享鎖) 
  2. SELECT ... LOCK IN SHARE MODE; 
  3. # 寫(xiě)鎖(X 鎖,排他鎖) 
  4. SELECT ... FOR UPDATE

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

鎖定讀驗(yàn)證:

為什么要有鎖定讀?

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

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

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

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

 

  1. start transaction with consistent snapshot; 

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

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

如果大家細(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操作都是先讀后寫(xiě),這個(gè)讀屬于鎖定讀(當(dāng)前讀)。

 

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

2017-07-25 14:38:56

數(shù)據(jù)庫(kù)一致性非鎖定讀一致性鎖定讀

2020-11-24 09:03:41

一致性MySQLMVCC

2011-05-04 10:19:13

MVCC

2011-05-04 09:43:23

當(dāng)前模式讀一致性讀

2023-08-14 08:10:33

CPU緩存RFO

2022-12-14 08:23:30

2021-07-27 08:57:10

算法一致性哈希哈希算法

2017-07-02 16:28:06

MySQL數(shù)據(jù)庫(kù)集群

2021-02-05 08:00:48

哈希算法?機(jī)器

2021-02-02 12:40:50

哈希算法數(shù)據(jù)

2019-11-01 09:13:37

算法哈希緩存

2020-05-12 10:43:22

Redis緩存數(shù)據(jù)庫(kù)

2022-03-22 09:54:22

Hash算法

2022-10-19 12:22:53

并發(fā)扣款一致性

2021-06-30 21:13:49

CPUCache數(shù)據(jù)

2021-02-04 06:30:26

Python編程語(yǔ)言

2022-04-06 15:19:32

數(shù)據(jù)庫(kù)MySQL一致性

2024-05-08 16:37:17

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

2022-02-17 21:04:27

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

2024-08-20 16:13:52

點(diǎn)贊
收藏

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