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

一文帶你搞懂MySQL的MVCC機制

 更新時間:2023年07月07日 11:15:36   作者:JAVA旭陽  
MySQL中的MVCC機制想必大家都有所耳聞吧,雖然在平時MySQL使用過程中基本上用不到,但是面試中出場率十分高,那么你對MVCC機制了解多少呢,MVCC機制是用來干嘛的呢,底層的工作原理是怎么樣的呢,本文就帶你一探究竟

MVCC機制是什么?

MVCC,英文全稱Multiversion Concurrency Control,多版本并發(fā)控制。簡單理解,就是相當(dāng)于給我們的MySQL數(shù)據(jù)庫拍個“快照”,定格某個時刻數(shù)據(jù)庫的狀態(tài)。

那你可能問為什么要拍個“快照”,也就是MVCC機制?

還記得事務(wù)的一大特性就是隔離性,一共有4個隔離級別,讀未提交,讀已提交,可重復(fù)讀,串行化。

MySQL InnoDB 引擎的默認(rèn)隔離級別可重復(fù)讀為例,可重復(fù)讀指一個事務(wù)執(zhí)行過程中看到的數(shù)據(jù),一直跟這個事務(wù)啟動時看到的數(shù)據(jù)是一致的。

為了保證事務(wù)啟動到結(jié)束整個生命周期看到的數(shù)據(jù)是一致的, 一般有兩種方案:

  • MySQL對數(shù)據(jù)“讀-寫”的時候,加鎖,其他事務(wù)寫這條數(shù)據(jù)時加上鎖,其他事務(wù)讀取的時候阻塞。
  • MySQL可以對事務(wù)啟動的時候,對數(shù)據(jù)庫拍個“快照”,那么事務(wù)運行過程中讀取都從這個快照讀取,不也是保證數(shù)據(jù)一致么。

第一種方案存在明顯的問題,加鎖會引發(fā)阻塞,從而降低數(shù)據(jù)庫性能。而MySQL設(shè)計者們采用第二種,也就是大名鼎鼎的MVCC,它不僅能夠解決不可重復(fù)讀,還一定程度解決幻讀的問題,因為你整個數(shù)據(jù)庫快照都有了,你就知道那個時刻的數(shù)據(jù)了。

雖然說SQL標(biāo)準(zhǔn)定義中可重復(fù)讀隔離級別下會存在幻讀的現(xiàn)象,但是不同的數(shù)據(jù)庫廠商可以基于SQL標(biāo)準(zhǔn)下有不同的實現(xiàn),那么不同隔離級別下發(fā)生的現(xiàn)象也會有出入,就拿MySQL的可重復(fù)讀隔離級別就可以一定程度保證幻讀。

小結(jié)一下:

MVCC在MySQL InnoDB中的實現(xiàn)主要是為了提高數(shù)據(jù)庫并發(fā)性能,用更好的方式去處理讀-寫沖突 ,做到即使有讀寫沖突時,也能做到不加鎖 , 非阻塞并發(fā)讀,而這個讀指的就是快照讀 , 而非當(dāng)前讀。

什么是快照讀和當(dāng)前讀?

前面提到了快照讀和當(dāng)前讀,這又有什么不一樣呢,什么樣的sql語句算是快照讀,什么樣的又算是當(dāng)前讀呢?

快照讀

快照讀又叫普通讀,也就是利用MVCC機制讀取快照中的數(shù)據(jù)。不加鎖的簡單的SELECT 都屬于快照讀,比如這樣:

SELECT * FROM user WHERE ...
  • 快照讀是基于MVCC實現(xiàn)的,提高了并發(fā)的性能,降低開銷
  • 大部分業(yè)務(wù)代碼中的讀取都屬于快照讀

當(dāng)前讀

當(dāng)前讀讀取的是記錄的最新版本,讀取時會對讀取的記錄進行加鎖, 其他事務(wù)就有可能阻塞。加鎖的 SELECT,或者對數(shù)據(jù)進行增刪改都會進行當(dāng)前讀。比如:

SELECT * FROM user LOCK IN SHARE MODE; # 共享鎖
SELECT * FROM user FOR UPDATE; # 排他鎖
INSERT INTO user values ... # 排他鎖
DELETE FROM user WHERE ... # 排他鎖
UPDATE user SET ... # 排他鎖
  • update、delete、insert語句雖然沒有select, 但是它們也會先進行讀取,而且只能讀取最新版本。

MVCC機制是咋工作的呢?

前面打個比方說MVCC機制相當(dāng)于是基于整個數(shù)據(jù)庫“拍了個快照”,這時,你會說這看上去不太現(xiàn)實啊。如果一個庫有 100G,那么我啟動一個事務(wù),MySQL 就要保存 100G 的數(shù)據(jù)出來,這個過程得多慢啊,而且也很占用空間啊,根本就不能支持幾個事務(wù)啊。別急,我們現(xiàn)在來講解下MVCC機制是如何工作的。

數(shù)據(jù)的多個版本

首先MySQL innoDB存儲引擎需要支持一條數(shù)據(jù)可以保留多個歷史版本。怎么保留呢?還記得事務(wù)日志undo log嗎?

對于使用 InnoDB 存儲引擎的數(shù)據(jù)庫表,它的聚簇索引記錄中都包含下面兩個隱藏列:

  • trx_id,當(dāng)一個事務(wù)對某條聚簇索引記錄進行改動時,就會把該事務(wù)的事務(wù) id 記錄在 trx_id 隱藏列里;
  • roll_pointer,每次對某條聚簇索引記錄進行改動時,都會把舊版本的記錄寫入到 undo 日志中,然后這個隱藏列是個指針,指向每一個舊版本記錄,于是就可以通過它找到修改前的記錄。

InnoDB 里面每個事務(wù)有一個唯一的事務(wù) ID,叫作 transaction id。它是在事務(wù)開始的時候向 InnoDB 的事務(wù)系統(tǒng)申請的,是按申請順序嚴(yán)格遞增的。

如上圖所示,針對id=1的這條數(shù)據(jù),都會將舊值放到一條undo日志中,就算是該記錄的一個舊版本,隨著更新次數(shù)的增多,所有的版本都會被 roll_pointer 屬性連接成一個鏈表,我們把這個鏈表稱之為版本鏈,根據(jù)版本鏈就可以找到這條數(shù)據(jù)歷史的版本。

一致性視圖ReadView

利用undo log日志我們已經(jīng)保留下了數(shù)據(jù)的各個版本,那么現(xiàn)在關(guān)鍵的問題是要讀取哪個版本的數(shù)據(jù)呢?

這時就需要用到ReadView了,ReadView就是事務(wù)在使用MVCC機制進行快照讀操作時產(chǎn)生的一致性視圖, 比如針對可重復(fù)讀隔離級別,是在事務(wù)啟動的時候,創(chuàng)建一個ReadView, 那ReadView種都有哪些關(guān)鍵信息呢?

  • trx_ids: 指的是在創(chuàng)建 ReadView 時,當(dāng)前數(shù)據(jù)庫中「活躍事務(wù)」的事務(wù) id 列表,注意是一個列表, “活躍事務(wù)”指的就是,啟動了但還沒提交的事務(wù)。
  • min_trx_id: 指的是在創(chuàng)建 ReadView 時,當(dāng)前數(shù)據(jù)庫中「活躍事務(wù)」中事務(wù) id 最小的事務(wù),也就是 m_ids 的最小值。
  • max_trx_id:這個并不是 m_ids 的最大值,而是創(chuàng)建 ReadView 時當(dāng)前數(shù)據(jù)庫中應(yīng)該給下一個事務(wù)的 id 值,也就是全局事務(wù)中最大的事務(wù) id 值 + 1;
  • creator_trx_id :指的是創(chuàng)建該 ReadView 的事務(wù)的事務(wù) id, 只有在對表中的記錄做改動時(執(zhí)行INSERT、DELETE、UPDATE這些語句時)才會為 事務(wù)分配事務(wù)id,否則在一個只讀事務(wù)中的事務(wù)id值都默認(rèn)為0。

對于當(dāng)前事務(wù)的啟動瞬間來說,讀取的一個數(shù)據(jù)版本的trx_id,有以下幾種可能:

  • 如果被訪問版本的trx_id屬性值與ReadView中的 creator_trx_id 值相同,意味著當(dāng)前事務(wù)在訪問它自己修改過的記錄,所以該版本可以被當(dāng)前事務(wù)訪問。
  • 如果落在綠色部分,表示這個版本是已提交的事務(wù)或者是當(dāng)前事務(wù)自己生成的,這個數(shù)據(jù)是可見的;
  • 如果落在紅色部分,表示這個版本是由將來啟動的事務(wù)生成的,是肯定不可見的;
  • 如果落在黃色部分,那就包括兩種情況
    • 若 數(shù)據(jù)的trx_idtrx_ids數(shù)組中,表示這個版本是由還沒提交的事務(wù)生成的,不可見, 去讀取這條數(shù)據(jù)的歷史版本,這條數(shù)據(jù)的歷史版本中都包含了事務(wù)id信息,去查找第一個不在活躍事務(wù)數(shù)組的版本記錄。
    • 若 數(shù)據(jù)的trx_id不在trx_ids數(shù)組中,表示這個版本是已經(jīng)提交了的事務(wù)生成的,可見。

這種通過版本鏈 + 一致性視圖 來控制并發(fā)事務(wù)訪問同一個記錄時的行為就叫 MVCC(多版本并發(fā)控制),現(xiàn)在你明白MySQL如何實現(xiàn)了“秒級創(chuàng)建快照”的能力了吧。

還是不懂?舉例說明

如果你對MVCC機制的整個流程還是比較模糊,我們現(xiàn)在舉例來說明下。

比如student表中有一個事務(wù)id為8的插入記錄:

insert into student(id, name, class) values(1, '張三', '一班')

我們現(xiàn)在在MySQL的讀已提交和可重復(fù)讀隔離級別下,MVCC機制的整個工作流程。

MySQL中的讀未提交和序列化并不需要MVCC機制,讀未提交,直接讀取別人未提交的數(shù)據(jù),而序列化全程用加鎖的方式,也用不上MVCC, 大家體會下。

可重復(fù)讀隔離級別下

可重復(fù)讀REPEATABLE READ 隔離級別的事務(wù)來說,只會在第一次執(zhí)行查詢語句時生成一個 ReadView ,之后的查詢就不會重復(fù)生成了。

begin/start transaction 命令并不是一個事務(wù)的起點,在執(zhí)行到它們之后的第一個操作 InnoDB 表的語句,事務(wù)才真正啟動。如果你想要馬上啟動一個事務(wù),可以使用 start transaction with consistent snapshot 這個命令。

事務(wù)10事務(wù)20事務(wù)30
beginUPDATE student SET name="李四" WHERE id=1;UPDATE student SET name="王五" WHERE id=1;
begin更新了一些其他表的數(shù)據(jù)
beginSELECT * FROM

事務(wù)10和20均未提交,現(xiàn)在事務(wù)30執(zhí)行select, 那么得到的結(jié)果是什么呢?

  • 在執(zhí)行select語句時會先生成一個ReadView,ReadView的trx_ids列表的內(nèi)容就是[10, 20],min_trx_id為10,max_trx_id為21,creator_trx_id為0。
  • 然后從版本鏈中挑選可見的記錄,從圖中看出,最新版本的列name的內(nèi)容是'王五',該版本的trx_id值為10,在trx_ids列表內(nèi),所以不符合可見性要求,根據(jù)roll_pointer跳到下一個版本。
  • 下一個版本的列name的內(nèi)容是'李四',該版本的trx_id值也為10,也在trx_ids列表內(nèi),所以也不符合要求,繼續(xù)跳到下一個版本。
  • 下一個版本的列name的內(nèi)容是'張三',該版本的trx_id值為8,小于ReadView中的min_trx_id值10,說明已經(jīng)提交了,那么最終返回'張三'。

讀已提交隔離級別下

讀已提交READ COMMITTED是每次讀取數(shù)據(jù)前都生成一個ReadView?;镜囊?guī)則和流程與可重復(fù)讀隔離級別一致,這里不做重復(fù)贅敘。

總結(jié)

本問重點介紹了MVCC機制,以及 MVCC 在 READ COMMITTD、 REPEATABLE READ 這兩種隔離級別的事務(wù)在執(zhí)行快照讀操作時訪問記錄的版本鏈的過程。這樣使不同事務(wù)的 讀-寫 、 寫-讀 操作并發(fā)執(zhí)行,從而提升系統(tǒng)性能。

  • READ COMMITTD 在每一次進行普通SELECT操作前都會生成一個ReadView
  • REPEATABLE READ 只在第一次進行普通SELECT操作前生成一個ReadView,之后的查詢操作都重復(fù)使用這個ReadView就好了。

以上就是一文帶你搞懂MySQL的MVCC機制的詳細(xì)內(nèi)容,更多關(guān)于MySQL MVCC機制的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • php mysql localhost,127.0.0.1和ip區(qū)別

    php mysql localhost,127.0.0.1和ip區(qū)別

    localhost與127.0.0.1的區(qū)別是什么?相信有人會說是本地ip,曾有人說,用127.0.0.1比localhost好,可以減少一次解析??磥磉@個入門問題還有人不清楚,其實這兩者是有區(qū)別的
    2014-05-05
  • Mysql?innoDB修改自增id起始數(shù)的方法步驟

    Mysql?innoDB修改自增id起始數(shù)的方法步驟

    本文主要介紹了Mysql?innoDB修改自增id起始數(shù)的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧<BR>
    2023-03-03
  • MySQL索引原理詳解

    MySQL索引原理詳解

    這篇文章主要介紹了MySQL索引原理詳解,索引是幫助MySQL高效獲取數(shù)據(jù)的排好序的數(shù)據(jù)結(jié)構(gòu),最重要的點是有序的,我們用索引就是為了快速的查找數(shù)據(jù),如果一堆數(shù)據(jù)是無序的,程序只能挨個遍歷每個元素
    2022-08-08
  • 簡單分析MySQL中的primary key功能

    簡單分析MySQL中的primary key功能

    這篇文章主要介紹了MySQL中的primary key功能,包括講到了其對InnoDB使用的影響,需要的朋友可以參考下
    2015-05-05
  • mysql中update按照多重條件進行更新處理的方案

    mysql中update按照多重條件進行更新處理的方案

    更新數(shù)據(jù)是使用數(shù)據(jù)庫時最重要的任務(wù)之一,下面這篇文章主要給大家介紹了關(guān)于mysql中update按照多重條件進行更新處理的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-09-09
  • MySQL深分頁問題及三種解決方案

    MySQL深分頁問題及三種解決方案

    本文主要介紹了MySQL深分頁問題及三種解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • Windows下MySQL定時備份腳本的實現(xiàn)

    Windows下MySQL定時備份腳本的實現(xiàn)

    這篇文章主要介紹了Windows下MySQL定時備份腳本的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • 解決Mysql服務(wù)器啟動時報錯問題的方法

    解決Mysql服務(wù)器啟動時報錯問題的方法

    這篇文章主要介紹了解決Mysql服務(wù)器啟動時報錯問題的方法,需要的朋友可以參考下
    2015-11-11
  • zabbix監(jiān)控MySQL主從狀態(tài)的方法詳解

    zabbix監(jiān)控MySQL主從狀態(tài)的方法詳解

    這篇文章主要介紹了zabbix--監(jiān)控MySQL主從狀態(tài)的方法,本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考借鑒價值 ,需要的朋友可以參考下
    2019-06-06
  • 基于mysql實現(xiàn)group by取各分組最新一條數(shù)據(jù)

    基于mysql實現(xiàn)group by取各分組最新一條數(shù)據(jù)

    這篇文章主要介紹了基于mysql實現(xiàn)group by取各分組最新一條數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-09-09

最新評論