日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

天龙源码框架分析_MySQL8-InnoDB总体架构和运行机制的系统分析(上)

發布時間:2025/3/20 数据库 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 天龙源码框架分析_MySQL8-InnoDB总体架构和运行机制的系统分析(上) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 前文回顧:四個階段和兩種方法


首先讓我們回顧下,在上一篇文章介紹的MySQL8代碼分析的四個階段和兩種方法。

四個階段: 借鑒瀑布式軟件開發流程,我們將從熟悉MySQL的使用和運維,到吃透MySQL8代碼的整個過程分為4個階段,如上圖所示。 其中第二階段是采用演繹法推導MySQL8總體架構和運行機制的過程, 而三、四階段合并起來,則是一個采用歸納法對總體架構中每一個模塊求解吃透的過程。

演繹法:演繹法適用于大型代碼分析工作中,直接用歸納法無法吃透代碼的情況。 此時以源碼為基礎,借助社區文檔,采用大膽假設小心求證的方法,梳理出大型軟件的核心運行機制,并將軟件的大規模代碼降解為若干個正交的小模塊,定義模塊之間的交互,再對每個模塊采用歸納法做梳理。

歸納法:當目標代碼規模不大時,采用從細節到整體的梳理方法,沿著代碼->結構->思路和方案->能力這個路徑來吃透代碼,最終理解軟件作者面對的問題、解決思路和解決方案,并形成熟練掌握、自如修改目標代碼的能力。


綜上,演繹法和歸納法并不是二選一相互替代的關系,而是在代碼分析的不同階段,適用不同的方法。演繹法從上而下根據代碼、社區文檔等材料推導出軟件的總體架構和運行機制,將軟件的大規模代碼降解為小模塊,進而可采用歸納法對小模塊做梳理和吃透。

2. 本文討論的內容和范圍


在上一篇文章中,我們提出用兩篇文章的篇幅來介紹我們團隊的MySQL8代碼分析方法。 前文是第一篇,主要討論了MySQL8代碼分析的難點和問題,以及我們對應的解決思路(四個階段和兩種方法);本文是第二篇,將以MySQL8 InnoDB為例,介紹如何采用演繹法求解InnoDB的總體架構和運行機制(說明:由于本文實際的內容很長,所以將文章分為上下兩篇發出。這一篇文章是上篇。為了行文方便,以下統一用 本文 指代這兩篇文章)。


之所以將討論范圍限定在InnoDB代碼, 是因為MySQL演進到MySQL8.0版本,實際上已經劃分為3個子系統:


在一篇文章里面將三個子系統的架構講清楚,是有些困難的。 而正如前文所述,我們希望用兩篇文章的篇幅,對我們團隊的MySQL代碼分析方法和思路做系統性地介紹,而不僅僅是給出分析的結果。現階段MySQL代碼還處于高速向前演進當中,代碼分析的結果可能會過時,但好的方法卻有更長遠的生命力,更有助于推進國內MySQL社區對MySQL代碼的理解。因此,我們選擇了MySQL8 InnoDB這一經典的存儲引擎作為代碼分析的目標,以此為例展開介紹我們的分析方法。


同時,在這篇文章中我們把InnoDB代碼的分析,限定在如何采用演繹法求解InnoDB的總體架構和運行機制上。 其原因正如上一篇文章所述, 當我們采用演繹法自上而下推導出InnoDB代碼的總體架構和核心運行機制后, InnoDB龐大的代碼便可以降解為一個個正交的子模塊,此后便可以采用歸納法對每個子模塊代碼做詳細分析并吃透。而歸納法,則是程序員在日常工作中不斷反復使用的,自然的代碼分析方法。


在后續的文章中,我們會繼續分析和討論MySQL8新增的數據字典子系統。而對于SQL Layer子系統的分析,由于時間有限,短期內不會有文章推出。我們期待有興趣的同學能夠參考我們介紹的方法,展開對SQL Layer的系統性分析,同時寫成文章提供給我們。投稿的方式推薦用知乎。也就是說,作者可以先在自己知乎賬號下把文章寫好并發表,然后推薦給我們。如果我們覺得合適,將建議作者投稿到這個專欄并最終發表。我們誠摯希望,和業內同仁一起共建最具系統性的MySQL8源碼分析文檔。

3. 本文的目標


所以,本文的目標是介紹如何采用演繹法求解InnoDB的總體架構和運行機制。 這句話包含兩個重點,一個是何為InnoDB的總體架構和運行機制, 另一個是如何采用演繹法求解。 本文只有把這兩點講透了,才能稱得把我們團隊的MySQL代碼分析方法做了系統性的介紹,能夠幫助DBA、后臺開發甚至MySQL內核開發同學更好地掌握MySQL內核代碼。

3.1 總體架構和運行機制
總體架構和運行機制,似乎是兩個非常寬泛的概念,兩者究竟包含什么內容,首先需要做一番探討。
我們在上一篇文章中提到,軟件源碼分析流程即為瀑布式軟件開發流程的逆過程:

從這點來看,總體架構和運行機制的分析,就是概要設計的逆過程。我們假設MySQL8的InnoDB代碼,是按照嚴格的瀑布式開發流程(需求分析->概要設計->詳細設計->編碼和測試)一次性開發測試完成。那么概要設計階段,需要輸出什么樣的文檔,才能夠指導并驅動后續階段的工作?我們認為, 這份概要設計文檔至少應該包含這幾方面的內容:
1.InnoDB的核心概念
2.InnoDB的對外接口
3.InnoDB的線程架構
4.InnoDB中庫表的數據組織方式,包含邏輯結構(內存中的結構)和物理結構(磁盤中的結構)
5.InnoDB的磁盤IO管理機制
6.InnoDB的數據字典
7.InnoDB的事務機制
8.InnoDB的模塊結構和模塊間交互

進一步地,上述8點內容可分為4類:
a. 概念結構和對外接口:1、2
b. 系統運行機制設計:3、5、7
c. 核心數據結構設計: 4、6
d. 代碼總體架構設計:8

進一步歸結為兩大類:
總體架構: a、c、d
運行機制:b

所以,概要設計階段所關注的8點內容, 都可歸納為總體架構和運行機制兩大類。 而總體架構和運行機制,正是代碼分析階段我們所要探討的。反過來說,當我們在代碼分析階段要探討InnoDB的總體架構和運行機制時, 可以轉換為對概要設計上述8點的探討。由此,我們定義清楚了總體架構和運行機制的詳細內容。

當然,嚴格的瀑布式開發流程只有理論上的意義,InnoDB實際的迭代過程,要比這個流程復雜太多。 但當我們面對一份靜態不變的MySQL8 InnoDB代碼時, 該假設對于我們求解InnoDB的總體架構和運行機制是適用的,它化繁為簡,讓我們抓住InnoDB代碼中最關鍵的部分,進而打開局面。

3.2 演繹法求解

以上8點是我們代碼分析工作的目標,是分析工作的輸出結果。但如果只是把結果給出而不給出分析的方法,就顯得過于單薄且缺乏誠意。 因此,我們的重點將放在如何把分析的過程給講透。

我們把分析的過程,稱之為演繹法求解,這其實是一個不那么嚴謹的說法。 學術上,演繹法(更準確的說法叫演繹推理 Deductive reasoning)是一個邏輯學上的概念,比如亞里士多德的三段論(大前提、小前提、結論)既演繹推理的一種。我們的方法并不是學術意義上的演繹推理,但和演繹推理具有相同的內涵,都強調通過從一般的、普遍的信息和知識中推導出更深層、更有價值的知識。

所以,沿著從一般普遍的知識到更深層、更有價值的知識這個推導思路,我們的演繹法求解InnoDB概要設計包含1個前提和4個步驟:

前提:前提包括源碼、社區文檔、經驗和基礎知識四個方面。 源碼指的就是MySQL源碼,我們團隊使用的是MySQL8.0.15版本; 社區文檔指MySQL內核的說明和分析文檔,包括MySQL8官方參考手冊、MySQL研發團隊WorkLog、和專欄第一篇文章提到的網站和博客等; 經驗則是指我們作為用戶去深度使用MySQL,吃透MySQL中的核心概念,深入了解每個概念含義后得到的經驗和知識,基礎知識則包括數據結構和算法、操作系統、數據庫系統原理等基礎知識。


作為用戶去深度使用MySQL,我們認為這對于MySQL源碼的分析和學習是非常重要的,同時不推薦沒有一定 MySQL 運維經驗的同學直接去啃MySQL源碼。因為可能對MySQL的一些概念和運行機制事實而非,同時也提不出比較深入的問題,不能帶著問題去分析源碼。
源碼、社區文檔、經驗和基礎知識,此四點都是一般性的知識,是有一定經驗的DBA或后臺開發都具備的, 以此為前提開始推導InnoDB的總體架構和運行機制,要求不算高,但如果采用我們的方法,成功的可能性卻足夠大。

步驟1-建立分析框架:有志于掌握MySQL內核的同學,第一個操作往往都是打開調試環境,深入debug MySQL執行一條SQL的過程。這是我們面對龐大MySQL源碼的第一反應,但并不是最有效的。 我們的做法是在InnoDB源碼分析之前,先根據數據庫系統的實現原理搭建出分析框架,通過這個分析框架來更有系統性分析和梳理源碼以及社區文檔,同時在分析和梳理的過程中,對該分析框架不斷求精和細化。
將InnoDB粗略劃分為若干個正交的大塊, 每個大塊代表InnoDB中的一塊重要功能,有了這個分析框架后,把InnoDB中的常見概念填入該分析框架,進一步得到概念框架;有了分析框架和概念框架后,則可在這兩個框架的指導下分析代碼和社區文檔,不斷挖掘和細化InnoDB的內部結構。

步驟2-數據對象分析:分析框架建立后,接下來要做的,就是通過閱讀代碼、閱讀社區文檔以及debug等方式,分析刻畫出InnoDB內部的數據對象和數據結構,包括兩個部分:磁盤中的數據對象和數據組織方式,以及內存中的數據結構。 不管是分析代碼和編寫代碼,都應該數據結構先行,有了清晰全面的數據結構,代碼邏輯和算法的梳理則顯得容易。

步驟3-運行機制分析:有了數據結構后,接下來就是要分析InnoDB內部的運行機制,包括:線程架構、SQL處理流程、事務機制、后臺機制、磁盤IO管理機制、數據字典。詳盡地分析梳理清楚InnoDB內部各種運行機制,這是一個規模龐大的工程,一是因為InnoDB包含的功能邏輯足夠多;二是一些功能邏輯經歷多年的優化,變得足夠復雜,需要大量的時間。但在概要設計求解階段,我們能夠以較粗的粒度,分析清楚以上6個運行機制,就足以幫助我們搞清楚InnoDB內部大概的運作方式,從而幫助后續的代碼詳細分析階段以子模塊為單位吃透代碼。

步驟4-代碼結構梳理:完成上述1-3步驟后,接下來就到了最后也是最難的一個步驟:代碼結構的梳理。最難的原因在于,InnoDB的代碼組織,可能是主流數據庫軟件中最無序和混亂的(比如相對于redis、MongoDB)。目前社區文檔中,對InnoDB內部數據結構和運行機制的剖析文章眾多,但系統性地梳理InnoDB代碼總體結構的文章近乎沒有(即使有參考價值也不大),可見該問題的難度之高。在本文(下篇)中,我們將嘗試以一個.cc為一個模塊單位,盡最大努力去描繪出InnoDB代碼的總體結構。


行文至此,本文要討論的兩個核心目標:1.總體架構和運行機制;2.演繹法求解總體架構和運行機制的4個步驟,就已經大致介紹完畢。下面我們將對演繹法的4個步驟,做詳細分析和討論,并在每個步驟的討論中,給出InnoDB的總體架構和運行機制8點內容中的對應該步驟的內容。

4. 建立最小范圍的InnoDB代碼分析框架


前面提到,建立分析框架的目的是為了更系統性地分析InnoDB源碼。InnoDB是一個結構化數據存儲引擎,所以我們當然可以根據結構化數據存儲引擎的一般特點,來構建InnoDB的分析框架。


任何一個存儲引擎,都需要提供三個基本操作接口,來操作基礎數據對象: 1.讀:支持基礎數據對象的讀取; 2.寫:支持基礎數據對象的插入、更新; 3.掃描:支持掃描某區間的基礎數據對象。


而所謂的基礎數據對象, 既該存儲引擎最基本的數據組織單元。比如,InnoDB的基礎數據對象是行(記錄),MongoDB則是文檔。存儲引擎的類型,亦往往按照基礎數據對象的特征來歸類。InnoDB之所以稱之為結構化數據存儲引擎,因為InnoDB中的行記錄遵循嚴格的schema規范;而MongoDB稱之為半結構數據存儲引擎,是因為Mongo中的文檔并沒有嚴格的schema規范,但有具備一定的結構性特征和約束。


考察任何一款存儲引擎,首要的是考察以上三個操作如何實現。 以這三個操作為線索去梳理該存儲引擎中的各種概念, 理清楚概念之間的聯系,即可建立出一個最小范圍的分析框架。
根據以上思路, 讓我們做進一步分析。


InnoDB是建立在文件系統之上的。文件系統亦是一個存儲引擎,向上層應用提供一段連續的存儲空間,并實現讀、寫、掃描三個功能:

讀:文件系統提供read接口, 運行上層應用在指定數據位置的情況下,快速地讀取某段數據; 寫:文件系統提供write接口,允許上層應用準確地將某段數據寫入到文件某個位置; 掃描:蘊含在讀功能中。
要考察和建立InnoDB的分析框架,可以從InnoDB對外提供的讀、寫、掃描操作如何轉換為文件系統的讀、寫、掃描操作這點來切入。從這點切入,我們建立出了InnoDB內部最基本的層次關系:

1.行(row): 行是InnoDB的基礎數據對象,InnoDB存儲引擎對外提供的讀、寫、掃描三大操作接口,都是以行為粒度來進行;

2.表(Table): 表是同一種類型的行記錄的集合。 有了表和行兩個概念后,關系型數據庫才具備了對真實世界數據的建模能力(亦有對表做歸類的需求,由此產生庫這個概念,但庫不在本文討論的主干路徑中,因此不做贅述)。 表是InnoDB對外提供的基本邏輯操作單元,所有結構化數據存儲和管理的語義,都可以映射為對表的操作和管理。

3.頁(page):頁是存放行的盒子,一個頁包含同屬于一張表的多條行記錄。頁的長度固定,在InnoDB中默認為16KB。行記錄在磁盤中的寫入和讀取,是以頁為單位進行。因此,即使SQL layer只想從磁盤中讀取一行,InnoDB也會把行所在的整個頁加載到內存。由于同一頁的行記錄屬于同一張表,且其他行有較大概率在接下來被訪問到,由此以頁為單位加載數據,具備較高的性能。

4.B+樹: B+樹是表的物理實現。表中的行記錄,在InnoDB中以B+樹的形式組織起來。對表的讀、寫、掃描操作,最終將轉換為對B+樹的讀、寫、掃描操作。B+樹是一種平衡多路查找樹,每一個節點都是一個數據頁,節點按照存儲的數據和作用的不同,分為兩種:葉節點和內節點。葉節點存儲真實的行記錄,內節點存儲行記錄的某些key值,和指向葉節點或下一層內節點的指針。B+樹中一條行記錄的搜索過程,是從最上層內節點(根頁)出發,利用該記錄的key值,和搜索到的每一個內節點上的key值數組做比較,進而得出行記錄所在的葉節點的位置,或下一層內節點的位置,然后再做進一步搜索。

5.文件:文件既普通磁盤文件。InnoDB管理的所有用戶數據都持久化在文件當中。行記錄和整顆B+樹,最終以page為單位落到文件中(后綴為 .ibd )。InnoDB數據文件以頁為單位做格式化, 比如1個16GB的數據文件,內部將按照16KB的大小,被格式化為1M個數據頁。ibd文件中除了存儲行記錄的page,還包括存儲其他信息的page,后面將進一步討論到。

至此,我們可以基本推斷出InnoDB引擎執行讀、寫、掃描三個基本操作的邏輯:

讀:InnoDB提供rnd_pos等行記錄讀接口來讀取1條記錄。InnoDB內部根據記錄的key遍歷B+樹,找到頁然后找到記錄并返回;

寫:InnoDB提供write_row/update_row等行記錄寫接口來插入/修改1條記錄。InnoDB內部根據記錄的key(主鍵)遍歷B+樹,找到記錄所在的頁,再到頁中插入1條記錄或更新頁中對應的那條記錄;

掃描:innodb提供rnd_next等行記錄掃描接口,供上層模塊讀取某范圍內的多條記錄。InnoDB內部先根據查詢條件到B+樹和頁中定位到第一條記錄,然后內部維持一個游標,每次調用時獲取游標指向的下一條記錄,直到結束范圍查詢。

5. 實用意義上的InnoDB代碼分析框架


通過上一小節的分析,我們搭建出了一個最小范圍的分析框架。但這個分析框架僅僅具備理論上的意義, 離真正的實用還有很大一段距離。在本小節中,我們將做進一步分析,來彌補這段距離。

5.1 索引、段、簇


索引用來提升對表中記錄的查詢性能,和表一樣都是關系型數據庫最常見的概念。 在InnoDB中,表和索引都是采用B+樹來表示。 換句話說, 邏輯層面的表對象和索引對象,在物理層面都對應到B+樹對象。 邏輯層面表和索引對象的創建、刪除和管理(擴容、縮容),都對應到物理層面B+樹對象的創建、刪除和管理(擴容、縮容)。


我們來進一步考察圍繞B+樹的管理操作。一個B+樹由一個或多個頁構成。創建一個B+樹,就是為該B+樹分配一個根頁;釋放一個B+樹,則是把該B+樹的所有頁回收;擴容就是給B+樹分配新的頁,縮容則是回收B+樹的一些頁。可見,對B+樹的管理,本質上是對文件中頁的管理。


從性能角度看,如果在B+樹需要一個頁時,才從文件中為其分配一個頁,并不是效率最高的做法。普遍的做法是,在B+樹和文件之間再設置 段 這樣一個虛擬的數據對象,將文件中的數據頁以段為單位組織起來,當B+樹需要數據頁時,向段申請,B+樹要釋放數據頁時,則釋放到段當中。


一個B+樹對應兩個段,分別是內節點段和葉節點段。內節點段負責管理B+樹內節點的數據頁; 葉節點負責管理B+樹葉節點的數據頁。B+樹需要創建新的內節點時,向內節點段申請,此時內節點段會先看下內部是否有空閑頁,有則將空閑頁賦給B+樹作為內節點;如果沒有空閑頁,則由段向文件提出申請,由文件為段分配空閑頁,段獲取到空閑頁之后,再將空閑頁分配給B+樹。B+樹需要創建新的葉節點時,邏輯類似。


由此又引出簇這樣一個概念。 一般情況下,文件為段分配空閑頁時,是以簇為單位來分配的。一個簇包含若干個連續的數據頁(InnoDB默認是64個)。以簇為單位分配的好處,一是將空閑數據頁批量預分配給段后,段中有大量空閑頁,進而提高給上層對象(比如B+樹)分配空閑頁的效率;二是由于簇中的數據頁在空間上是連續的,這樣分配給B+樹的頁,則在空間上也是相鄰的,從而通過一次性預讀多個頁的方法,可以加快從文件加載數據頁的效率。


至此,我們從使用原理的角度出發,梳理了索引、段、簇3個概念,理清了它們出現的背景和邏輯。這些概念和行、表、頁、B樹、文件這5個概念之間的關系如下圖所示:

其中,行、頁、文件 為3種實體數據對象, B+樹、表、索引、段、簇為5種虛擬數據對象。實體數據對象承載真正的數據, 虛擬數據對象則是數據結構,實現了實體對象的管理機制。

5.2 表空間


接下來,我們考察一下表空間這個概念。如上圖所示, 不管是表對應的B+樹還是索引對應的B+樹, 其數據頁最終都存儲在文件中。那么,表和文件的數量有何的對應關系? 是1:1(一張表(及其索引)對應一個文件), 還是N:1(多張表對應一個文件),亦或1:N(一張表對應多個文件)?


對于現階段主流的文件系統(ext3、ext4、NTFS等)而言,單個文件的存儲量能夠到16TB及以上,因此把所有表(及其索引)數據放到1個文件中,似乎問題不大。但是回到Heikki Tuuri剛開始設計InnoDB的1995年,甚至Oracle、DB2剛推出的七八十年代,文件系統單個文件的容量遠沒有這么大(比如FAT32最大文件為4GB),如果表和文件是1:1 或者N:1的關系, 那么單張表的數據量則不能大于4GB,這對于工業級別的數據庫系統顯然是不能接受的。


為此,表和文件必須是1:N或者M:N的關系。 早期InnoDB的設計是M:N,既設計一個共享表空間,該表空間為多個文件的集合, 并將所有數據庫上的所有表存儲到該表空間中。 從MySQL5.6.6版本以來,開始推出獨立表空間功能,既每張用戶表可以對應1個獨立的表空間,該獨立表空間下面只有1個文件(由于文件系統的升級,設置多個文件的意義已經不大)。
將表空間的概念和原理引入到我們的分析框架,得到新的框架如下圖所示:

5.3 數據頁緩沖池

上面我們考察了InnoDB中主要的幾種數據對象,這些數據對象可以分為兩類,一類是實體數據對象,如行、頁、文件;一類是虛擬數據對象,如表/索引、B+樹,段、簇、表空間。實體數據對象承載了真正的數據,而虛擬數據對象則是將實體數據對象組織起來的一種數據結構,分別對應不同的使用場景。比如B+樹組織了頁和行,用于InnoDB的讀、寫、掃描三大操作的實現;段和簇用于頁分配回收和管理;表和索引則是作為行記錄的邏輯集合,為上層應用讀、寫、掃描行記錄,以及管理行集合提供了簡單和易于理解的概念和機制。表空間的作用其實是將多個實體文件捏合成一個連續的空間,并對外提供空間數據的分配機制。

InnoDB中還有另外一個重要的虛擬數據對象,那就是數據頁緩沖池(page buffer pool)。顧名思義, 數據緩沖池就是內存中緩存數據頁的buffer。緩存數據頁的目的有兩個,一個是緩存被讀到過的數據頁,以提高二次讀的性能;第二個是緩存被修改的數據頁,避免每次數據頁被修改后都刷入到磁盤,從而避免大量隨機寫(而真正的持久化機制,則是通過事務提交時redolog落盤加page定時持久化并推進check point的方式來實現,后面將進一步介紹)。

所以,在考慮數據頁緩沖池后,我們進一步升級分析框架如下圖所示:

至此,我們推導出的分析框架涵蓋了InnoDB中行記錄讀、寫、掃描操作,以及數據頁分配、回收和管理操作,將InnoDB數據存儲、訪問和流轉的主航道做了詳細刻畫。

5.4 事務的A和D

在分析推導完InnoDB數據流轉主航道后,我們把分析方向轉移到InnoDB的事務機制。事務是業務向數據庫發起的一連串的,數量有限的讀寫操作。當這些操作執行完成后,上層應用看到的結果和數據庫系統數據的最終狀態,要滿足 ACID 4個特性。 在一個結構化存儲系統中, 事務完整支持 = ACID的完整保證,ACID 4個特性全面又相互正交地覆蓋了事務操作對數據庫系統的全部要求。ACID可以劃分為兩類:

  • A和D:對應數據庫系統的崩潰恢復機制;
  • I:對應數據庫系統的并發控制機制。
  • 可能讀者看到這里會有一些疑惑:C(一致性)上哪去了? 數據庫的一致性難道跟并發控制或崩潰恢復沒有關系嗎?臟讀、不可重復讀等問題,難道不是跟一致性有關的問題嗎?

    必須要指出的是,在經典的數據庫事務理論中,C(一致性)和并發控制沒有半毛錢關系。I(隔離性)的保證,覆蓋了多線程環境下事務并發控制問題的全部(包括臟讀、不可重復讀等問題的解決)。C(一致性)只和數據庫的正確性有關,舉兩個和C有關的例子:

    1.在單機數據庫里,一個事務到A賬戶扣了50塊錢,到B賬戶加了50塊錢,那么C(一致性)的保證就是數據庫的代碼是正確的,事務執行完成后,A賬戶少了50塊錢,B賬戶多了50塊錢;

    2.在分布式數據庫里,如果A賬戶和B賬戶位于兩個不同的數據庫節點,那么為了保證一致性,就需要引入兩階段提交等分布式事務控制機制,但該場景跟并發控制并無任何關系。

    總的來說,數據庫事務對C(一致性)的要求,其實就是要求數據庫必須是一個邏輯上寫對的程序,同時能夠正確處理內部的多節點分區。在MySQL8.0中,跟C(一致性)有關且值得討論的,基本只有內部兩階段提交了,但該問題由于過于細節,不在本文的討論范圍中。

    在這一節,我們先來討論事務的崩潰恢復機制,既A和D的保證。A代表原子性,要求一個事務完成后,該事務對數據庫系統數據的修改操作,要么全部執行,要么都不執行;D則代表一個事務提交后,該事務修改的數據必須全部持久化。在InnoDB中,采用redolog + 前滾和undolog + 回滾,來實現事務的A和D。

    為了搞清楚這一點, 我們可以分析兩個概念(redolog、undolog)和兩個操作(事務執行時保證A和D的一般流程,崩潰恢復的一般流程)。

    redolog: redolog 是 InnoDB 中 page 修改操作的 Write AHead Log(WAL) 。 WAL出現的原因,是為了獲取更好的寫性能。InnoDB在修改 page 之前將生成一份redolog ,該redolog忠實記錄了對page的修改操作。 當事務提交時,該 redolog 將被持久化到 redolog 文件,但修改后的 page 并不持久化。這樣做的好處,是將持久化 page 產生的隨機寫,轉換寫 redolog 的順序寫,由于傳統磁盤順序寫性能遠好于隨機寫,因此 WAL 賦予了存儲引擎更好的寫性能。

    當然,由于事務提交 page 不持久化,就導致了磁盤中 page 的版本,會落后于內存中的版本,此時如果進程或機器崩潰,將會出現數據丟失。為解決這個問題,InnoDB 在崩潰后的恢復流程中,將重放 redolog 文件中的redolog,利用 redolog 將 page 更新為崩潰時刻內存中的那個版本。 這個操作稱之為前滾。

    undolog: 如果只有redolog+前滾,還不足以保證事務的A和D。 redolog記錄的是page修改后的新值, 如果事務在崩潰前成功提交,利用redolog可以把page修改為最終版本;但如果在修改page后,事務在崩潰前未成功提交,此時為了原子性,則需要撤銷事務對page的修改。為了解決這一問題,則需引入undolog+回滾。

    從原理上看,一條 undolog 記錄了事務修改的某條表記錄的舊值,可以認為當delete一條記錄時,undo log中會記錄一條對應的insert記錄,反之亦然;當update一條記錄時,它記錄一條對應相反的update記錄。在崩潰恢復階段,先利用 redolog + 前滾將所有 page 恢復到崩潰時的那個版本,然后再讀取每一條 undolog ,利用 undolog 來撤銷未提交事務在相應記錄上的修改。

    從實現角度看,undolog 實際上也存放在page中,因此undolog page并不是在事務提交時立即刷盤,而是和表page一樣延遲刷盤。而undolog page的持久化保證亦是通過redolog:在事務生成一條undolog之前, 會先生成對應的redolog,該redolog在事務提交時,將和事務產生其他redolog一樣,被刷入磁盤。崩潰恢復階段,利用redolog前滾完成后,表page和undolog page都被恢復到了崩潰時的版本, 此時利用undolog page 中的每條undolog,去撤銷未提交事務對表page的修改。

    事務執行時保證A和D的一般流程: 為了保證事務的A和D, 事務在執行過程中需要做以下事情:

  • 在更新一條記錄前,先生成一條undolog,而這又分為兩個步驟:
    1.1 為生成undolog這個操作,生成redolog;
    1.2 生成一條undolog;
  • 更新一條記錄,分為兩個步驟:
    2.1 為記錄更新操作,生成redolog;
    2.2 更新這條記錄;
  • 如果事務提交,則進入提交流程,將事務生成的redolog刷入磁盤;
  • 如果事務回滾,則進入回滾流程,利用第1步生成的undolog回滾該記錄更新操作。同時為該回滾操作生成redolog,事務在回滾結束前必將redolog刷入磁盤,從而保證回滾操作的持久性。
  • 崩潰恢復的一般流程: 正如前面提到,崩潰恢復通過redolog前滾和undolog后滾,來保證事務的原子性。具體的流程如下:

  • 從redolog文件中記錄的check point位點開始,掃描redolog文件到末尾,讀取每一條redolog;
  • 根據讀取到的redolog做前滾,將page(表page、undolog page、系統page等)恢復到崩潰時刻的版本;
  • 前滾結束后,將前滾中修改過的所有page都強制刷盤;
  • 讀取undolog所在的數據結構:回滾段,并從中恢復出所有崩潰前的活躍事務;
  • 以事務為單位,利用undolog回滾所有崩潰前的活躍事務(開啟一個后臺線程異步進行);
  • 對于狀態出于prepare的事務,結合上層binlog中的事務狀態來決定是否回滾;
  • 處理因為崩潰導致中斷的ddl操作;
  • 結束崩潰恢復流程。
  • 至此, InnoDB中事務AD保證的基本原理和主干邏輯,已經梳理完畢。從原理和邏輯層面理清后,我們接著來看下相關的代碼實現,如何整合到InnoDB的整體分析框架中。

    經過梳理,我們得到考慮事務A&D保證的InnoDB分析框架如下:

    5.5 事務的I

    梳理完A和D,我們再來看下I,也就是InnoDB事務的并發控制機制。數據庫事務對I(隔離性)的要求,體現了對事務并發控制的要求。

    通常意義上的并發控制,指的是多線程訪問共享資源時,避免讀寫沖突的方法。 由此推廣到事務場景,那就是多線程并發執行事務操作時,避免共享資源讀寫沖突的方法。事務并發控制的關鍵,在于如何對并發執行的多個事務做正確調度,保證每個事務能夠正確地寫數據/讀到正確的數據。 在數據庫理論上,采用沖突可串行化來衡量事務并發調度的效果。

    數據庫對并發執行的多個事務的一個調度,如果該調度可通過不斷交換兩個相鄰的無沖突操作,最終轉為為一個串行調度,則稱該并發調度為沖突可串行化調度。

    如果數據庫每一次對并發事務的調度,都做到了沖突可串行化調度,則事務的隔離性得到了嚴格的保證。但這種嚴格調度的代價太大,導致事務的并發程度大為降低,從而嚴重影響事務處理性能。為此,才有了隔離級別的概念。隔離級別是對嚴格隔離性的弱化或妥協,目的是降低可串行化要求,進而提升系統的并發度。數據庫的四個隔離級別如下:

    串行化:在該級別下,隔離性被嚴格保證,并發事務的調度結果等價于一個串行化調度; 可重復讀:隔離性相對串行化有所弱化,能夠保證事務多次讀取某條數據時讀到相同的數據,但存在幻讀問題; 讀提交:隔離性進一步弱化,能夠保證事務讀到的數據都是已提交的數據,但不能保證可重復讀; 讀未提交:隔離性最弱。只能夠保證寫的一致性,但不能保證讀提交。

    通過以上分析,我們從理論上對隔離性這個概念做了一次梳理。接下來我們將進一步討論隔離性在InnoDB中如何保證,也就是InnoDB的事務并發控制機制。

    業界事務并發控制的實現思路,主流的有以下兩種:
    1.鎖:既對共享資源做必要的保護;
    2.時間戳:比較事務和記錄的時間戳,根據兩者大小來解決事務沖突;

    值得一提的是, 多版本并發控制(MVCC)并不屬于鎖或者時間戳任何一種。MVCC作為一種技術,甚至無法和上述四個隔離級別聯系起來。 原因在于四個隔離級別是在MVCC技術之前出現的提出的,出發點是在只有鎖、時間戳等技術的前提下,逐級弱化可串行化的要求,提高事務并發執行效率。而MVCC出現在四個隔離級別提出之后,隨后和鎖等并發控制機制相結合,去進一步完善數據庫對事務的并發調度。因此,有了MVCC、ICC(基于索引的并發控制)等技術之后,主流數據庫對事務并發控制的實現,遠比經典理論總結的要復雜。關于這塊的細節,可參考李海翔:《數據庫事務處理的藝術》一書。

    InnoDB的并發控制,則完全建立在鎖保護 + MVCC的基礎上,而MVCC,我們未嘗不可視之為一種廣義的鎖(鎖既多個線程之間的契約)。為此,我們只需要跟著鎖走,把InnoDB 相關的機制梳理清楚,則事務的并發控制機制自然水落石出。

    先不考慮MVCC,InnoDB的鎖可分為兩大類,Lock和Latch。 Lock是和事務并發控制有關的鎖,而Latch則是和多線程并發爭用資源有關的鎖。兩者具體含義和區別如下:

    因此,涉及事務隔離性和隔離級別的保證, 只涉及到Lock,對應的模塊就是InnoDB的鎖系統。 在我個人看來, InnoDB的鎖系統(含MVCC的實現),是基礎軟件中罕見的精品,Heikki Tuuri把給事務加鎖這個事情做成了一門藝術,值得每一位有志于基礎軟件研發的同學好好學習。


    InnoDB啟動開發是在1995年左右。此時事務封鎖的機制已經是學術上一個被充分研究的領域,事務相關的概念和模型,比如ACID四個正交特性、多粒度封鎖、兩階段封鎖、讀沖突的幾種情況和4個隔離級別等,都在學術上得到了透徹的討論,從而有效地指導工業實現(當然這并不是說,事務封鎖機制是先有完整的學術理論,然后再落地到工業界。相反,以Jim Gray為代表的數據庫事務理論奠基者們,一直是在工業界開展工作,工業界大量的技術創新在他們卓越研究能力的催生下,才誕生了堪稱偉大的數據庫事務理論。正如Gray在《事務處理:概念和技術》中所言,“事務處理是一個實踐引導理論的研究領域,通常在商業系統實現了某些思想很久以后,該思想才會在學術界出現”)。


    所以1995年左右當Heikki開始寫InnoDB時, 事務處理已經是成熟的技術,而Oracle、DB2亦是珠玉在前。按理說Heikki Tuuri只需要按照成熟方法去做一版工整的實現即可,但Heikki卻通過設計上的巧思和精湛的實現,在Oracle、DB2之后發出了自己的聲音,成就又一個經典。

    在我看來,InnoDB鎖系統(含MVCC)的經典之處在于:

    1.一鎖多用的行鎖設計。InnoDB中的行鎖對象 struct lock_rec_t 不僅僅是行鎖,也是gap鎖,next-key鎖,甚至是頁鎖。傳統意義上,行鎖的定義非常簡單,就是作用于行上的一把X鎖(比如Oracle的行鎖),其加鎖原理來自于兩階段封鎖機制。而gap鎖、next-key鎖的(用于解決RR隔離級別下幻讀的問題),其設計思想更多是來自于前面提到的基于索引的并發控制技術。從這個角度看,InnoDB相當于是用 lock_rec_t 這樣一種數據結構,刻畫了更多類型的鎖。另外,在事務做范圍查詢或順序掃描時,一把InnoDB的 lock_rec_t 對象又起到了頁鎖的效果,可用很少的空間記錄一個數據頁中的各行記錄的加鎖情況。


    究其根本,還是在于InnoDB中的行鎖,并不是傳統意義上的行鎖,而是索引鎖。在InnoDB的設計中,B+樹索引是表數據存儲的底層物理數據結構,表的數據既一個聚集索引。所以在鎖系統的設計中,InnoDB采用表鎖+索引鎖,而不是傳統意義的表鎖+行鎖設計,也是順理成章的事情。

    2.打破常規的并發度和隔離性平衡方法。我們知道,四個事務隔離級別出現的原因,是為了最求更高的事務并發度,從而對分級別弱化嚴格隔離性。四個事務隔離級別是事務處理技術的經典法則,甚至被納入到ANS的在SQL標準規范中。但是,個人認為,Heikki在設計InnoDB鎖系統時,可能根本就沒去考慮RC級別該怎么實現,RR級別該怎么實現,他的目標只有1個:利用MVCC技術,做一個并發度最高,隔離性最好的鎖系統,有了這個設計后,才開始考慮兼容4個隔離級別。所以我們才會看到,InnoDB默認隔離級別是RR,在OLTP場景多數情況下,InnoDB的RR隔離級別性能要比RC隔離級別高這樣的情況。通知RR隔離級別通過GAP鎖、Next鎖等機制,可一定程度避免幻讀問題。


    綜合以上兩點來看,我們會發現InnoDB鎖系統背后,存在極簡這樣的一個設計理念,這個理念我個人認為也是整個InnoDB的設計理念。比如表數據既一個聚集索引,一鎖多用的行鎖設計,打破常規的并發度和隔離性平衡,基于寫入字節數的LSN設計等,都體現了這一點。


    極簡帶來的好處有很多,有實現成本上的,也有美學上的。從實現成本來看,不得不說InnoDB的代碼是極為精煉的,相同的代碼量包含的信息,要比其他存儲引擎多得多。而更少代碼意味著維護成本更低(當然反過來說也對閱讀者和開發者的能力提出了更高的要求)。從美學上來看,極簡一般來說總是更好的。可以說,極簡是任何一個技術門類發展的必然趨勢。在前人經典設計的基礎上,總會有后來者打破常規,用更簡潔的方式進行設計,在實現功能、穩定性、性能和性價比的同時,達到美學上的新高度。寫到這里,想起知乎上有一個問題:建筑如何輕盈起來,頗能對該現象做進一步闡述。


    回到InnoDB鎖系統的代碼實現,我們可以看到InnoDB鎖系統的數據結構設計,是非常簡單清晰的。總共來說只有兩個基礎數據對象(lock_t 和 ReadView,其中lock_t把行鎖和表鎖折疊在一起)和三個全局結構(lock_sys->rec_hash, dict_table_t::locks 和 trx_sys->mvcc)。其中:
    lock_sys->rec_hash: 為存儲行鎖對象的hash表;
    dict_table_t::locks: dict_table_t對象中的locks鏈表,存儲該表當前的所有表鎖信息。dict_table_t對象,是InnoDB中表數據字典對象,一張表只有1個dict_table_t對象,所有的dict_table_t對象存儲在dict_sys->table_hash 中;
    trx_sys->mvcc: InnoDB中的MVCC總控結構,內含兩個ReadView鏈表:m_free 和m_views。其中m_views存儲了當前被所有事務對象(trx_t)正在使用或暫時擁有的ReadView對象;m_free為空閑ReadView對象。


    在事務對象(trx_t)中,維護了一個lock結構,指向該事務對象擁有的所有表鎖信息和行鎖信息; 同時維護了一個read_view對象,指向該事務正在使用或者暫時擁有的ReadView。


    經過梳理,我們進一步得到考慮事務ACID保證的InnoDB分析框架如下:

    6. 結語

    本文是《MySQL8-InnoDB總體架構和運行機制的系統分析》的上篇。 主要內容分為兩塊,首先在前一篇文章的基礎上,進一步闡述了我們團隊的MySQL代碼分析方法,并給出了分析工作的4個步驟(3.2節-演繹法求解); 第二,詳細介紹了4個步驟的第一個步驟(建立分析框架),我們團隊所做的工作,給出了我們的InnoDB代碼分析框架。

    本文的下篇將詳細介紹我們團隊在第2到第4個分析步驟所做的事情以及產出。具體包括:

    步驟2-數據對象分析;

    步驟3-運行機制分析;

    步驟4-代碼結構梳理。

    敬請期待。

    作者:UCloud云數據庫內核團隊

    總結

    以上是生活随笔為你收集整理的天龙源码框架分析_MySQL8-InnoDB总体架构和运行机制的系统分析(上)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 超碰.com | 女儿的朋友在线播放 | 久久依人网 | 国产资源网 | 欧美性猛交99久久久久99按摩 | 天干夜夜爽爽日日日日 | 黄色特级片 | 青草视频在线免费观看 | 后进极品白嫩翘臀在线视频 | 成人在线欧美 | 琪琪av在线 | 成年人在线视频网站 | 国产精品一区二区三区四区五区 | 久久免费成人 | 日韩福利在线视频 | 欧美黄色免费 | 超碰97最新 | jizzjizz8| 国产精品入口66mio男同 | 插插宗合网 | 最新色网站 | 99久久久无码国产 | 都市激情亚洲色图 | av不卡网| 91视频在线免费看 | 毛茸茸成熟亚洲人 | 免费一区二区在线观看 | 成人黄色激情网 | 国产精品四虎 | 色爽影院 | 日本xxxx在线观看 | 香蕉av网站 | 精品久久久久久久 | 亚洲av无码一区二区三区性色 | 国产精品一区二区无码免费看片 | 欧美日韩免费一区二区三区 | 欧美成人黄 | 好吊色综合 | 96超碰在线 | 欧美日韩国产一区 | 日韩视频免费观看高清完整版在线观看 | 久久久久久激情 | √天堂资源在线 | 国产精品一区二区三区免费在线观看 | 美女被草视频在线观看 | 国产操| 黄色网址你懂的 | 黑人3p波多野结衣在线观看 | 最新天堂中文在线 | 水果派解说av | 麻豆精品国产传媒 | 欧美一区二区三区成人精品 | 国产在线精品视频 | 伊人春色在线观看 | 欧美视频你懂的 | 中文字幕在线观看欧美 | 黄色av电影在线观看 | 在线免费黄色网 | 欧美一区二区三区在线视频 | 牛牛精品一区二区 | 尤物影院在线观看 | 日韩精品在线一区 | 国产三级全黄 | av男人天堂av | 孕妇疯狂做爰xxxⅹ 国产精品乱码久久久久久 99久久久成人国产精品 | 欧美色一区二区三区在线观看 | 欧美大片一区二区三区 | 午夜18视频在线观看 | 国产精品久久久久久久久久久久久久 | 欧美一区二区三区系列电影 | 国产又粗又长又硬免费视频 | 91一二区 | 成人区人妻精品一区二区不卡视频 | 五月婷婷伊人网 | 伊人婷婷久久 | kendra lust free xxx| 夜夜骑天天操 | 波多野结av衣东京热无码专区 | 我把护士日出水了视频90分钟 | 嫩草视频在线观看视频 | 久综合网| 看片一区二区 | 亚洲 欧美 视频 | 在线视频免费观看一区 | 精品人妻一区二区三区麻豆91 | 日日夜夜精品视频免费 | 欧美中文字幕在线观看 | 自拍视频在线观看 | 91久久电影 | 成人一区二区三区仙踪林 | 福利视频免费看 | 久久国产一区二区 | 欧美女优在线观看 | 狠狠操综合网 | 91青青青 | 欧美日韩综合一区二区 | 亚洲av综合色区 | 色悠悠国产 | 欧美一级一区二区三区 |