欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

mysql中的隔離性原理詳解

 更新時(shí)間:2023年08月09日 08:29:59   作者:meihaoshy  
這篇文章主要介紹了mysql隔離性的原理,多版本并發(fā)控制(MVCC)是一種用來(lái)解決?讀-寫(xiě)沖突?的無(wú)鎖并發(fā)控制,為事務(wù)分配單向增長(zhǎng)的事務(wù)ID,為每個(gè)修改保存一個(gè)版本,版本與事務(wù)ID關(guān)聯(lián),讀操作只讀該事務(wù)開(kāi)始前的數(shù)據(jù)庫(kù)的快照,需要的朋友可以參考下

數(shù)據(jù)庫(kù)并發(fā)的三種場(chǎng)景

  • 讀-讀 :不存在任何問(wèn)題,也不需要并發(fā)控制
  • 讀-寫(xiě) :有線(xiàn)程安全問(wèn)題,可能會(huì)造成事務(wù)隔離性問(wèn)題,可能遇到臟讀,幻讀,不可重復(fù)讀
  • 寫(xiě)-寫(xiě) :有線(xiàn)程安全問(wèn)題,可能會(huì)存在更新丟失問(wèn)題

在這三種場(chǎng)景中 讀-讀幾乎沒(méi)有任何問(wèn)題 所以我們不需要并發(fā)控制

寫(xiě)-寫(xiě)并發(fā)只需要加鎖控制即可

所以說(shuō)我們今天重點(diǎn)討論下讀-寫(xiě)并發(fā)

MVCC

基本介紹

多版本并發(fā)控制( MVCC )是一種用來(lái)解決 讀-寫(xiě)沖突 的無(wú)鎖并發(fā)控制

為事務(wù)分配單向增長(zhǎng)的事務(wù)ID,為每個(gè)修改保存一個(gè)版本,版本與事務(wù)ID關(guān)聯(lián),讀操作只讀該事務(wù)開(kāi)始前的數(shù)據(jù)庫(kù)的快照。 所以 MVCC 可以為數(shù)據(jù)庫(kù)解決以下問(wèn)題

  • 在并發(fā)讀寫(xiě)數(shù)據(jù)庫(kù)時(shí),可以做到在讀操作時(shí)不用阻塞寫(xiě)操作,寫(xiě)操作也不用阻塞讀操作,提高了數(shù)據(jù)庫(kù)并發(fā)讀寫(xiě)的性能
  • 同時(shí)還可以解決臟讀,幻讀,不可重復(fù)讀等事務(wù)隔離問(wèn)題,但不能解決更新丟失問(wèn)題

在我們理解MVCC之前 我們需要知道三個(gè)前提知識(shí)

  • 3個(gè)記錄隱藏字段
  • undo 日志
  • Read View

我們下面就分別先介紹下這三個(gè)隱藏字段

三個(gè)前提知識(shí)介紹

三個(gè)隱藏字段

  • DB_TRX_ID :6 byte,最近修改( 修改/插入 )事務(wù)ID,記錄創(chuàng)建這條記錄/最后一次修改該記錄的事務(wù)ID
  • DB_ROLL_PTR : 7 byte,回滾指針,指向這條記錄的上一個(gè)版本(簡(jiǎn)單理解成,指向歷史版本就行,這些數(shù)據(jù)一般在 undo log 中)
  • DB_ROW_ID : 6 byte,隱含的自增ID(隱藏主鍵),如果數(shù)據(jù)表沒(méi)有主鍵, InnoDB 會(huì)自動(dòng)以DB_ROW_ID 產(chǎn)生一個(gè)聚簇索引
  • 實(shí)際還有一個(gè)刪除flag隱藏字段, 既記錄被更新或刪除并不代表真的刪除,而是刪除flag變了

假設(shè)我們現(xiàn)在創(chuàng)建并且插入了一條數(shù)據(jù) 代碼和顯示如下

mysql> create table if not exists student(
name varchar(11) not null,
age int not null
);
mysql> insert into student (name, age) values ('張三', 28);
Query OK, 1 row affected (0.05 sec)
mysql> select * from student;
+--------+-----+
| name | age |
+--------+-----+
| 張三 | 28 |
+--------+-----+
1 row in set (0.00 sec)

實(shí)際上在Linux隱藏字段的效果就是

在這里插入圖片描述

對(duì)于上圖做出一定解釋

  • 假設(shè)插入的事務(wù)ID是9 那么TRX_ID字段實(shí)際上就是9
  • 因?yàn)檫@是我們插入的第一個(gè)數(shù)據(jù) 所以說(shuō)隱式主鍵就是1
  • 因?yàn)檫@是第一個(gè)數(shù)據(jù) 沒(méi)有更前面的數(shù)據(jù)了 所以說(shuō)回滾指針指向的就是空
  • 其實(shí)還有其他的隱藏字段 比如說(shuō)flag等 上面沒(méi)有標(biāo)識(shí)出

undo log日志

mySQL 將來(lái)是以服務(wù)進(jìn)程的方式,在內(nèi)存中運(yùn)行。我們之前所講的所有機(jī)制:索引,事務(wù),隔離性,日志等,都是在內(nèi)存中完成的,即在 MySQL 內(nèi)部的相關(guān)緩沖區(qū)中,保存相關(guān)數(shù)據(jù),完成各種判斷操作。然后在合適的時(shí)候,將相關(guān)數(shù)據(jù)刷新到磁盤(pán)當(dāng)中的。

所以,我們這里理解undo log,簡(jiǎn)單理解成,就是 MySQL 中的一段內(nèi)存緩沖區(qū),用來(lái)保存日志數(shù)據(jù)的就行。

read view 快照

關(guān)于快照讀的知識(shí)下面模擬MVCC場(chǎng)景的時(shí)候會(huì)講

Read View就是事務(wù)進(jìn)行 快照讀 操作的時(shí)候生產(chǎn)的 讀視圖 (Read View),在該事務(wù)執(zhí)行的快照讀的那一刻,會(huì)生成數(shù)據(jù)庫(kù)系統(tǒng)當(dāng)前的一個(gè)快照,記錄并維護(hù)系統(tǒng)當(dāng)前活躍事務(wù)的ID(當(dāng)每個(gè)事務(wù)開(kāi)啟時(shí),都會(huì)被分配一個(gè)ID, 這個(gè)ID是遞增的,所以最新的事務(wù),ID值越大)

Read View 在 MySQL 源碼中,就是一個(gè)類(lèi),本質(zhì)是用來(lái)進(jìn)行可見(jiàn)性判斷的。 即當(dāng)我們某個(gè)事務(wù)執(zhí)行快照讀的時(shí)候,對(duì)該記錄創(chuàng)建一個(gè) Read View 讀視圖,把它比作條件,用來(lái)判斷當(dāng)前事務(wù)能夠看到哪個(gè)版本的數(shù)據(jù),既可能是當(dāng)前最新的數(shù)據(jù),也有可能是該行記錄的 undo log 里面的某個(gè)版本的數(shù)據(jù)。

下面是 ReadView 結(jié)構(gòu),但為了減少同學(xué)們負(fù)擔(dān),我們簡(jiǎn)化一下

class ReadView {
	// 省略...
private:
	/** 高水位:大于等于這個(gè)ID的事務(wù)均不可見(jiàn)*/
	trx_id_t m_low_limit_id;
	/** 低水位:小于這個(gè)ID的事務(wù)均可見(jiàn) */
	trx_id_t m_up_limit_id;
	/** 創(chuàng)建該 Read View 的事務(wù)ID*/
	trx_id_t m_creator_trx_id;
	/** 創(chuàng)建視圖時(shí)的活躍事務(wù)id列表*/
	ids_t m_ids;
	/** 配合purge,標(biāo)識(shí)該視圖不需要小于m_low_limit_no的UNDO LOG,
	* 如果其他視圖也不需要,則可以刪除小于m_low_limit_no的UNDO LOG*/
	trx_id_t m_low_limit_no;
	/** 標(biāo)記視圖是否被關(guān)閉*/
	bool m_closed;
	// 省略...
};
m_ids; //一張列表,用來(lái)維護(hù)Read View生成時(shí)刻,系統(tǒng)正活躍的事務(wù)ID
up_limit_id; //記錄m_ids列表中事務(wù)ID最小的ID
low_limit_id; //ReadView生成時(shí)刻系統(tǒng)尚未分配的下一個(gè)事務(wù)ID,也就是目前已出現(xiàn)過(guò)的事務(wù)ID的最大值+1
creator_trx_id //創(chuàng)建該ReadView的事務(wù)ID

我們?cè)趯?shí)際讀取數(shù)據(jù)版本鏈的時(shí)候,是能讀取到每一個(gè)版本對(duì)應(yīng)的事務(wù)ID的,即:當(dāng)前記錄的DB_TRX_ID 。

那么,我們現(xiàn)在手里面有的東西就有,當(dāng)前快照讀的 ReadView 和 版本鏈中的某一個(gè)記錄的DB_TRX_ID 。

那么 現(xiàn)在我們的問(wèn)題就是 當(dāng)前快照讀,應(yīng)不應(yīng)該讀到當(dāng)前版本記錄。一張圖,解決所有問(wèn)題!

在這里插入圖片描述

如果查到不應(yīng)該看到當(dāng)前版本,接下來(lái)就是遍歷下一個(gè)版本,直到符合條件,即可以看到。上面的readview 是當(dāng)你進(jìn)行select的時(shí)候,會(huì)自動(dòng)形成。

看到這里有的同學(xué)可能會(huì)產(chǎn)生這樣一個(gè)疑問(wèn) 如何遍歷下個(gè)版本呢?

我們之前說(shuō)過(guò) undo log其實(shí)就是一個(gè)緩沖區(qū) 并且里面有著回滾指針連接著的各種數(shù)據(jù) (實(shí)際上就是單鏈表連接的各種數(shù)據(jù))

再次總結(jié)下

  • 我們第一次開(kāi)啟事務(wù)的時(shí)候會(huì)生成一個(gè)read view的結(jié)構(gòu)體
  • 該結(jié)構(gòu)體中會(huì)記錄活躍的最小事務(wù)id和比最大事務(wù)id還要大一的事務(wù)id
  • 當(dāng)我們第一次select讀的時(shí)候會(huì)形成一個(gè)快照
  • 如果說(shuō)當(dāng)前版本的TRX_ID小于最小的id 那么我們就可以見(jiàn)到
  • 如果當(dāng)前版本的TRX_ID大于等于最大ID我們就不能見(jiàn)到
  • 如果說(shuō)在最小和最大區(qū)間里面 并且該TRX_ID不是活躍ID(已提交) 則我們可以看到
  • 如果說(shuō)在最小和最大區(qū)間里面 并且TRX_ID還是活躍ID(未提交) 則我們不能看到

轉(zhuǎn)化成現(xiàn)實(shí)中的例子

現(xiàn)在的我們能夠看到我們出生之前所有人寫(xiě)的作品 但是我們不能看到還未出生的人寫(xiě)的作品

如果說(shuō)寫(xiě)書(shū)的人跟我們同一個(gè)時(shí)代 我們就要判斷這本書(shū)有沒(méi)有發(fā)表 (是否提交) 如果提交了我們就能看見(jiàn) 如果沒(méi)有提交 我們就不能看見(jiàn)

模擬MVCC場(chǎng)景

MVCC場(chǎng)景中有增刪改查 下面我們分別進(jìn)行討論

我們插入的時(shí)候只需要形成一條新的undo log版本鏈 將回滾指針指向前面的數(shù)據(jù) 如果需要回滾直接通過(guò)回滾指針找到需要覆蓋的數(shù)據(jù)進(jìn)行覆蓋即可

我們前面說(shuō)過(guò)了 mysql中還有一個(gè)隱藏的falg字段 因此 如果需要?jiǎng)h除的話(huà) 只需要將flag標(biāo)志位設(shè)置即可

這是最麻煩的一個(gè)環(huán)節(jié) 我們使用一個(gè)例子來(lái)說(shuō)明MVCC中的改

現(xiàn)在一個(gè)表中有如下的記錄

在這里插入圖片描述

現(xiàn)在有一個(gè)事務(wù)ID為10的事務(wù) 要修改表中的name張三為李四

  • 因?yàn)橐薷?所以我們肯定要先給記錄上鎖
  • 修改之前 我們要將改之前的數(shù)據(jù)拷貝一份要undo log當(dāng)中 假設(shè)地址為0x11223344
  • 之后我們修改原始數(shù)據(jù)中name為李四 并且將回滾指向0x11223344這個(gè)地址
  • 事務(wù)10commit提交 釋放鎖

過(guò)程圖如下

在這里插入圖片描述

如果還有事務(wù)要修改新的數(shù)據(jù)就參考上面的步驟即可

于是乎我們就形成了一條基于鏈表記錄的歷史版本鏈 undo log里面的一個(gè)個(gè)歷史版本就稱(chēng)為快照

現(xiàn)在我們明白了

  • 所謂的回滾其實(shí)就是拿歷史版本鏈中的某條數(shù)據(jù)覆蓋當(dāng)前數(shù)據(jù)

首先我們要理解兩個(gè)概念 當(dāng)前讀和快照讀

  • 當(dāng)前讀:讀取最新的記錄,就是當(dāng)前讀。增刪改,都叫做當(dāng)前讀,select也有可能當(dāng)前讀,比如select lock in share mode(共享鎖), select for update
  • 快照讀:讀取歷史版本??煺兆x不會(huì)被加鎖。

多個(gè)事務(wù)同時(shí)增刪改的時(shí)候是當(dāng)前讀 需要加鎖 在串行化的隔離級(jí)別下 select也是當(dāng)前讀 需要加鎖

如果select是快照讀 那么和增刪改的當(dāng)前讀不沖突 所以說(shuō)并行效率高 事務(wù)的隔離級(jí)別決定了select是當(dāng)期讀還是快照讀 具體的判斷方法可以參考前面read view部分的知識(shí)

RR和RC的區(qū)別

RR級(jí)別測(cè)試

演示一 兩邊開(kāi)啟事務(wù) 右邊先進(jìn)行快照讀 左邊插入數(shù)據(jù)之后commit 右邊再進(jìn)行快照讀和當(dāng)前讀

在這里插入圖片描述

在這里插入圖片描述

我們可以發(fā)現(xiàn)的是 當(dāng)右邊使用快照讀的時(shí)候不管左邊有沒(méi)有commit 讀取到的數(shù)據(jù)是一樣的

而使用當(dāng)前讀的時(shí)候 我們可以發(fā)現(xiàn)讀取的數(shù)據(jù)就是最新的數(shù)據(jù)了 光靠這個(gè)一個(gè)試驗(yàn)我們看不出來(lái)什么 接下來(lái)我們看演示二

演示二: 左右兩邊同時(shí)開(kāi)啟一個(gè)事務(wù) 左邊先插入數(shù)據(jù)之后提交 右邊在左邊提交之后進(jìn)行快照讀

在這里插入圖片描述

我們發(fā)現(xiàn) 這個(gè)時(shí)候右邊的快照讀 讀取了最新的數(shù)據(jù)

對(duì)比這兩次試驗(yàn)加上之前的read view部分學(xué)習(xí)我們不難做出以下的推斷

在RR級(jí)別下 第一次select快照讀的時(shí)候會(huì)生成一個(gè)read view快照 之后的讀取就按照這個(gè)快照進(jìn)行

而實(shí)際上在RC級(jí)別中 每一次的select快照都都會(huì)生成一個(gè)最新的read view快照

所以說(shuō)RR和RC最本質(zhì)的區(qū)別就是 RR只會(huì)生成依次read view快照 而RC快照讀幾次就會(huì)生成幾次快照

四種隔離級(jí)別的不同處理方式

讀–未提交

直接當(dāng)前讀 不加鎖

串行化

當(dāng)前讀 加鎖

讀 提交

在RC級(jí)別中 每次的select讀取都是快照讀 每次都會(huì)生成一個(gè)最新的read view快照

可重復(fù)讀

在RR級(jí)別中 每次select讀取都是快照讀 并且都會(huì)遵循第一次select讀取時(shí)生成的read view快照

總結(jié)

在這里插入圖片描述

到此這篇關(guān)于mysql隔離性的原理的文章就介紹到這了,更多相關(guān)mysql隔離性?xún)?nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • mysql 8.0.11安裝教程圖文解說(shuō)

    mysql 8.0.11安裝教程圖文解說(shuō)

    本文通過(guò)圖文并茂的形式給大家介紹了mysql 8.0.11安裝教程,非常不錯(cuò),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下
    2019-04-04
  • MySQL安裝提示配置信息已損壞請(qǐng)聯(lián)系技術(shù)人員

    MySQL安裝提示配置信息已損壞請(qǐng)聯(lián)系技術(shù)人員

    為了重新安裝MySql,看別人的博客說(shuō)在注冊(cè)表中搜索mysql,全部刪除。再安裝時(shí)提示配置信息已損壞,遇到這個(gè)問(wèn)題怎么處理呢,下面小編給大家?guī)?lái)了詳細(xì)解決方法,感興趣的朋友一起看看吧
    2023-01-01
  • 最新評(píng)論