InnoDB存儲引擎對MVCC的實現

MVCC

MVCC的目的

在搞清楚MVCC之前,我們要搞懂一個問題,MVCC到底解決的是什么問題?

我用一句話概括,那就是為了解決讀-寫可以一起的問題!

在我們的印象里,InnoDB可以讀讀并發,不能讀寫并發,或者寫寫并發

這是很正常的想法,因為如果讀寫并發的化,會有并發問題

而對于寫寫并發來說,我們還是得加鎖,這是沒辦法改的

但是對于讀-寫并發來說,我們就可以用MVCC來解決

所以,綜上所述,MVCC就是為了解決讀-寫并發,提高性能.

更準確的說,它是針對讀操作的!和寫操作關系不大

所以在MVCC的加持上,如果要解決并發問題?
讀 + MVCC
寫 + 鎖

解決哪些并發問題?

先說結論:
對于讀已提交

MVCC 解決了臟讀

對于可重復讀
MVCC 解決了臟讀 + 不可重復讀 + 幻讀


然后其次,我們要搞清楚,有四種隔離級別,我相信大家都知道

讀未提交 什么都沒解決
讀已提交 解決了臟讀
可重復讀 解決了臟讀 + 不可重復讀
串行 解決了臟讀 +不 可重復讀 + 幻讀

這我相信你也是滾瓜爛熟

那么對于MVCC來說,它實際解決了哪些問題呢? 這個我們就得分情況了,因為MVCC也是貼合四種隔離級別的!我先說這個就是為了有一個大致的方向

需要注意的是,對于MVCC來說,讀未提交 和 串行是束手無策的

你想一想就知道,讀未提交連臟讀都可以容忍,它還要你MVCC來干嘛,直接讀就可以了
對于串行來說,它的讀 + 寫都是串行化的,不管你讀還是寫,都要排隊,根本沒有讀 - 寫并發的情況

所以,我們需要明確一點,MVCC是針對讀已提交 + 可重復讀

想清楚這一點,我們再來分別看,MVCC解決的問題,你可能會問,既然你都是貼合四種隔離級別的,那不就是對應隔離級別解決的問題嘛,不是的,這里就是有一個特殊,所以我才會拿來說

對于讀已提交

MVCC 解決了臟讀

對于可重復讀
MVCC 解決了臟讀 + 不可重復讀 + 幻讀

那么比較特殊的一點就是,對于可重復讀來說,多解決了一個幻讀的問題

綜上所述,我們的結論就是,MVCC是針對讀已提交 + 可重復讀,并且在可重復讀的情況下,還多解決了一個幻讀問題

如何實現的?

讀到這里,你就很好奇了,它是如何實現的呢?

其實很簡單,就是一句話,MVCC讀的是副本,不是當前的當前正在并發的數據

我們來反過來想,為什么會出現臟讀 + 不可重復讀,就是因為事務在并發的時候,有可能會修改你正在讀的數據,這就導致,讀第二次的時候會出現不一致

那我們該如何解決呢?也很簡單,就是在事務一開始的時候,保存一份當前數據的副本,這樣就算其他事務修改了,它修改的是數據庫的數據,而不是我們的副本,這樣不就避免了這兩個問題嗎?

順著這個思路想,如果每次事務一開始的時候,我們都要取保存一份當前數據的副本,那如果有上億的讀的訪問的話,那不是內存炸了?

所以,我們得用MySQL現成的功能來實現這個副本的功能!

所以,自然而然你會想到,Undo log,只有它存著歷史的數據,那就是剛剛好的

至此,我們大致就可以引出了三個依賴
第一,隱藏字段
第二,Undo log
第三,Read View

Undo log就不用說了,就是為了生成副本的
Read View 也好說,不過是副本的官方的名字
對于隱藏字段來說,我們就得說一說了

對于聚簇索引來說,他有必要的隱藏列,
trx_id: 事務id
roll_pointer: 指針

事務id,就不用多說了,你需要一個標識來標識事務
對于roll_pointer來說,它和Undo log就會有聯系,就是一個指針嘛,我們直接看圖就能懂了,為什么需要roll_pointer,還有Undo log實際上長什么樣子
在這里插入圖片描述
一看就懂,不就是鏈表嘛,第一個就是最新的記錄,后邊的都是歷史記錄

此時,你就大概能明白,為什么需要隱藏字段了把,其實也不是很重要,一筆帶過就行

我們特別要注意的是,Read View 副本的結構,這才是我們需要研究的東西
因為我們不熟啊

Read View結構

對于Read View我們先看圖,它的大致結構如下

在這里插入圖片描述
實際,它記錄的不是實際的數據,而是一群活躍的事務id

什么叫活躍的事務id呢?就是那些在并發的時候,在那搗亂的事務,為啥要記錄呢?很好理解嘛,在Undo log中,只要有這些搗蛋鬼做的記錄,我們就不看,讀那些已經提交的記錄

結構

creator_trx_id: 創建Readview的事務id
trx_ids: 活躍的事務id列表
up_limit_id: 最小的事務ID
low_limit_id: 下一個最大的事務ID

這里需要特別注意,up_limit_id是最小的
low_limit_id是最大的

還有一點是,這里的最小最大的選取標準是不一樣的
這里舉個例子,比較容易懂

比如說,事務id列表中,有三個事務id, 1,3,5

我們要假設MySQL只有這三個事務id,其他沒有,暗含的意思是,這里的id = 5,是當前生成的最大的事務id

那么up_limit_id 就是1
low_limit_id 是當前生成過的最大的事務id + 1 也就是 5 + 1 = 6

假設事務5提交了,此時的事務列表是1,3
low_limit_id 還是6,因為它類似于Auto_increment 自增id的意思,它會記錄當前生成過的最大的事務ID

ReadView的規則

我們研究這個規則,實際上就是研究MVCC的核心規則了

我們得對應著Undo log的版本鏈來看,版本鏈就是歷史記錄

如下
在這里插入圖片描述
然后就著
ReadView的結構來看

在這里插入圖片描述

事務開始,生成一個ReadView,
然后當前事務,要讀取的時候,此時就會去查看兩個東西 ReadView + Undo log

查看Undo log就是順著鏈表一個一個往下看,找到符合條件的就返回

符合的條件就是規則

第一,如果此時查看的記錄trx_id = creator_trx_id,符合條件,讀此記錄
意思就是,當前記錄就是我們自己事務干的事,ReadView是我們當前事務自己生成的,自己讀自己的,當然沒問題

第二,如果不是自己的,就檢查,ReadView的trx_ids,也就是事務id列表
如果發現,當前訪問的記錄,就是這些搗蛋鬼其中一個干的,那就是不符合條件,如果發現不在這群事務id里邊,說明,當前讀的是,已經提交了的記錄,符合條件

第三,需要注意的是,對于第二,來說,當前的記錄的trx_id是介于
up_limit_id < 當前 事務id < low_limit_id,

up_limit_id,和low_limit_id,是一個范圍,還有一個功能就是快速的判斷

如果,最新的記錄是小于up_limit_id的,那么直接就是返回當前記錄

為什么呢?因為事務id是單調遞增的,和主鍵id一樣,如果我們最新的記錄都提交了,那更早的記錄是不是都提交了,返回返回沒問題!

如果,最新的記錄是大于等于low_limit_id的,那么不符合條件,往下找
為什么呢? 因為,如果此時最新的記錄是大于low_limit_id的,意思就是這個記錄它是在我們事務生成ReadView之后生成的,也就是

我們生成ReadView
然后緊接著,有一個事務進來修改了

那更不可能讀這條記錄了,比遲到的人還遲到.

綜上所述,我們總結一下
trx_id = creator_trx_id,可以讀
當trx_id < up_limit_id,可以讀
當trx_id >= low_limit_id,不可以讀
當up_limit_id <= trx_id < low_limit_id的時候,查看當前記錄的id是不是在trx_ids里邊,如果在的話,說明這是搗蛋鬼干的,不可以讀,如果不存在,可以讀

再總結一下,我們只要記住什么時候不可以讀

當trx_id >= low_limit_id 的時候,不可以讀
當trx_id在范圍中間,且當前的記錄id,在trx_ids里邊,不可以讀
其他情況都可以讀

讀已提交 和 可重復讀的不同流程

因為,MVCC要貼合這兩個隔離級別解決的問題,所以,他們的流程會有點不一樣

對于讀已提交來說,事務開始,每一次select都會去獲取ReadView
對于可重復讀來說,事務開始,到結束,只會獲取一次ReadView

這個想想就能明白,讀已提交,沒有解決不可重復讀問題,那么每一次讀ReadView的時候,有可能有一些事務就提交了,會讀到多的數據

可重復讀的時候,由于至始至終,只看一個ReadView,那就是事務開始的時候的ReadView,那可想而知,不會有可重復讀的問題,還有幻讀的問題了,因為,只看一個ReadView,就算多一萬遍,讀到的記錄還是不會多不會少,所以這也是為什么對于可重復讀來說,它可以解決幻讀問題了.

總結

所以我們最后來總結一下

MVCC的目的是什么?

為了解決讀-寫并發,但是只針對讀已提交 + 可重復讀

MVCC是怎么實現的?

第一,隱藏字段-> 事務id + 指針
第二,Undo log
第三,ReadView

如何實現這個問題,就轉換成ReadView如何做校驗?

對于讀已提交,每次select的時候,獲取一個ReadView
對于可重復讀,事務開始的時候,獲取一個ReadView

做實際的校驗

到最后,做校驗的問題,就轉換成校驗的規則是什么?

如果,trx_id >= low_limit_id,不符合條件
如果,up_limit_id <= trx_id < low_limit_id,并且trx_id在trx_ids中,不符合條件
其他情況都符合

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/717841.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/717841.shtml
英文地址,請注明出處:http://en.pswp.cn/news/717841.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

帶壓縮路徑的并查集

find帶壓縮路徑的并查集 int fa[]; void init(int _size) {for(int i0;i<_size;i){fa[i] i;} } int find(int aim) {int cur aim;while (fa[aim] ! aim){aim fa[aim];}while (fa[cur] ! cur){int tmp cur;cur fa[cur];fa[tmp] aim;}return aim; } void join(int a,in…

構建安全的REST API:OAuth2和JWT實踐

引言 大家好&#xff0c;我是小黑&#xff0c;小黑在這里跟咱們聊聊&#xff0c;為什么REST API這么重要&#xff0c;同時&#xff0c;為何OAuth2和JWT在構建安全的REST API中扮演著不可或缺的角色。 想象一下&#xff0c;咱們每天都在使用的社交媒體、在線購物、銀行服務等等…

file-upload-download

方式一 情況1&#xff1a; PostMapping("/download1")public ResponseEntity<byte[]> download1() throws Exception {// 下載文件目錄位置FileInputStream fis new FileInputStream("C:\\Users\\wsd\\Pictures\\susu.jpg");// 一次讀取bytes.leng…

Sqli-labs靶場第16關詳解[Sqli-labs-less-16]自動化注入-SQLmap工具注入

Sqli-labs-Less-16 #自動化注入-SQLmap工具注入 SQLmap用戶手冊&#xff1a;文檔介紹 - sqlmap 用戶手冊 以非交互式模式運行 --batch 當你需要以批處理模式運行 sqlmap&#xff0c;避免任何用戶干預 sqlmap 的運行&#xff0c;可以強制使用 --batch 這個開關。這樣&#xff0…

【視頻編碼\VVC】多樣化視頻編碼工具了解

除了通用編碼工具&#xff0c;VVC還針對特定特性的全景視頻、屏幕視頻開發了特定的編碼工具。 全景視頻編碼 360度全包圍視角的球面視頻。為了采用傳統的視頻編碼&#xff0c;全景視頻需要轉換為平面視頻&#xff0c;經緯度等角映射&#xff08;ERF&#xff09;、立方體映射&…

PostgreSQL操作筆記

基礎操作 數據庫相關 -- 查看所有數據庫 \l-- 切換到指定數據庫 \c 庫名-- 查看庫中所有表 \d執行SQL腳本 如果有現成的SQL腳本&#xff1a; \i 腳本路徑路徑一般需要用單引號引起來。 如果需要當場編輯一次性的SQL腳本&#xff0c;可以&#xff1a; \e執行上述命令后會進…

GC機制以及Golang的GC機制詳解

要了解Golang的GC機制,就需要了解什么事GC,以及GC有哪幾種實現方式 一.什么是GC 當一個電腦上的動態內存不再需要時&#xff0c;就應該予以釋放&#xff0c;以讓出內存&#xff0c;這種內存資源管理&#xff0c;稱為垃圾回收&#xff08;Garbage Collection&#xff09;&#x…

最長上升子序列(LIS)簡介及其例題分析

一.最長上升子序列&#xff08;LIS&#xff09;的相關知識 1.最長上升子序列&#xff08;Longest Increasing Subsequence&#xff09;&#xff0c;簡稱LIS&#xff0c;也有些情況求的是最長非降序子序列&#xff0c;二者區別就是序列中是否可以有相等的數。假設我們有一個序…

【論文筆記】Initializing Models with Larger Ones

Abstract 介紹權重選擇&#xff0c;一種通過從預訓練模型的較大模型中選擇權重子集來初始化較小模型的方法。這使得知識從預訓練的權重轉移到更小的模型。 它還可以與知識蒸餾一起使用。 權重選擇提供了一種在資源受限的環境中利用預訓練模型力量的新方法&#xff0c;希望能夠…

代碼隨想錄Day67 | 695.島嶼的最大面積 1020.飛地的數量

代碼隨想錄Day67 | 695.島嶼的最大面積 1020.飛地的數量 695.島嶼的最大面積1020.飛地的數量 695.島嶼的最大面積 文檔講解&#xff1a;代碼隨想錄 視頻講解&#xff1a; 狀態 采用bfs&#xff0c;這道題相較于之前的題變為了求島嶼的最大面積。那就說明我們每遇到一個新的島嶼…

【Linux】軟件管理yum | 編輯器vim | vim插件安裝

目錄 1. Linux軟件管理yum 1.1 什么是軟件包 1.2 查看軟件包 1.3 如何安裝軟件 1.4 如何卸載軟件 2. Linux編輯器vim 2.1 vim的基本概念 2.2 vim的基本操作 2.3 vim正常模式命令集 2.4 vim末行模式命令集 2.5 簡單vim配置 2.6 插件安裝 1. Vim-Plug 3. coc.nvim …

如何自己系統的學python

學習Python是一項很好的投資&#xff0c;因為它是一種既強大又易于學習的編程語言&#xff0c;適用于多種應用&#xff0c;如數據分析、人工智能、網站開發等。下面是一個系統學習Python的步驟建議&#xff1a; 基礎準備 安裝Python&#xff1a; 訪問Python官網下載最新版本的…

微服務獲取當前登錄用戶信息

一&#xff0c;實現思路 1&#xff0c;基于JWT令牌登陸方式 JWT實現登錄的&#xff0c;登錄信息就保存在請求頭的token中。因此要獲取當前登錄用戶&#xff0c;只要獲取請求頭&#xff0c;解析其中的token。 1&#xff09;&#xff0c;Gateway網關攔截&#xff0c;解析用戶信…

微信小程序-生命周期

頁面生命周期 onLoad: 頁面加載時觸發的方法&#xff0c;在這個方法中可以進行頁面初始化的操作&#xff0c;如獲取數據、設置頁面狀態等。 onShow: 頁面顯示時觸發的方法&#xff0c;在用戶進入頁面或從其他頁面返回該頁面時會調用此方法。可以在此方法中進行頁面數據刷新、動…

Onenote軟件新建筆記本時報錯:無法在以下位置新建筆記本

報錯現象&#xff1a; 當在OneNote軟件上&#xff0c;新建筆記本時&#xff1a; 然后&#xff0c;嘗試重新登錄微軟賬戶&#xff0c;也不行&#xff0c;提示報錯&#xff1a; 解決辦法&#xff1a; 打開一個新的記事本&#xff0c;復制粘貼以下內容&#xff1a; C:\Users\Adm…

Mysql中的事務

什么是事務&#xff1a; 多條sql語句&#xff0c;要么全部成功&#xff0c;要么全部失敗。 事務的特性&#xff1a; 1&#xff1a;原子性(Atomic)&#xff1a; 組成一個事務的多個數據庫操作是一個不可分割的原子單元&#xff0c;只有所有操作都成功&#xff0c;整個事務才會…

在Unity中模擬實現手勢識別功能

在虛擬現實(VR)和增強現實(AR)的應用開發中&#xff0c;手勢識別技術扮演著至關重要的角色&#xff0c;它允許用戶以自然的方式與虛擬世界進行交云。然而&#xff0c;并非所有開發者都有條件使用真實的手勢識別硬件。本文介紹了如何在Unity中通過模擬的方式實現一個簡單的手勢識…

【LeetCode】1768_交替合并字符串_C

題目描述 給你兩個字符串 word1 和 word2 。請你從 word1 開始&#xff0c;通過交替添加字母來合并字符串。如果一個字符串比另一個字符串長&#xff0c;就將多出來的字母追加到合并后字符串的末尾。 返回 合并后的字符串 。 https://leetcode.cn/problems/merge-strings-al…

C++調用lua函數

C 調用Lua全局變量(普通) lua_getglobal(lua, "width");int width lua_tointeger(lua,-1);lua_pop(lua,1);std::cout << width << std::endl;lua_close(lua); 這幾行代碼要放到lua_pcall(lua, 0,0,0);之后才可以. C給lua傳遞變量 lua_pushstring(lua, …

Python 操作 Excel,如何又快又好?

?數據處理是 Python 的一大應用場景&#xff0c;而 Excel 則是最流行的數據處理軟件。因此用 Python 進行數據相關的工作時&#xff0c;難免要和 Excel 打交道。Python處理Excel 常用的系列庫有&#xff1a;xlrd、xlwt、xlutils、openpyxl ?xlrd &#xff0d; 用于讀取 Exce…