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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Innodb隔离级别的实现原理

發(fā)布時間:2024/9/20 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Innodb隔离级别的实现原理 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Mysql簡介

版本號3.23(2001Mysql的誕生,引入MyISAM和InnoDB。

?

版本號4.0(2003支持更多語法,如UNION和多表DELETE語法,引入查詢緩存。

?

版本號5.0(2006出現(xiàn)企業(yè)級Mysql特性:視圖,觸發(fā)器,存儲過程和存儲函數(shù)。之后Sun收購Mysql后,5.1版本,引入分區(qū)和基于行的復(fù)制備份,以及可插拔的存儲引擎API。

?

版本號5.5(2010)Oracle收購Sun以后,將InnoDB設(shè)為默認(rèn)存儲引擎,增加了其擴(kuò)展性和性能提升。

?

版本號5.6(2013為InnoDB加入全文檢索。

?

Mysql 服務(wù)器架構(gòu)

Mysql的整體架構(gòu)如下圖所示,分為三層:

第一層為大部分應(yīng)用都擁有的client端或者接口端,主要負(fù)責(zé)調(diào)用Mysql服務(wù)器的服務(wù)。

第二層為Mysql服務(wù)器層,其中包含了所有在調(diào)用存儲引擎之前所做的預(yù)備工作

第三層為存儲引擎層,該層和服務(wù)器層是完全分離,并且由上面可以知道已經(jīng)實現(xiàn)了存儲引擎可插拔的模式

?

Mysql模塊架構(gòu)圖

?

?

?

?

?

1、Client& Server 交互協(xié)議模塊

任何C/S 結(jié)構(gòu)的軟件系統(tǒng),都肯定會有自己獨有的信息交互協(xié)議,MySQL 也不例外。MySQL的Client & Server 交互協(xié)議模塊部分,實現(xiàn)了客戶端與MySQL 交互過程中的所有協(xié)議。當(dāng)然這些協(xié)議都是建立在現(xiàn)有的OS 和網(wǎng)絡(luò)協(xié)議之上的,如TCP/IP 以及Unix Socket。

?

2、初始化模塊

顧名思議,初始化模塊就是在MySQL Server 啟動的時候,對整個系統(tǒng)做各種各樣的初始化操作,比如各種buffer,cache 結(jié)構(gòu)的初始化和內(nèi)存空間的申請,各種系統(tǒng)變量的初始化設(shè)定,各種存儲引擎的初始化設(shè)置,等等。

?

3、網(wǎng)絡(luò)交互模塊

底層網(wǎng)絡(luò)交互模塊抽象出底層網(wǎng)絡(luò)交互所使用的接口api,實現(xiàn)底層網(wǎng)絡(luò)數(shù)據(jù)的接收與發(fā)送,以方便其他各個模塊調(diào)用,以及對這一部分的維護(hù)。所有源碼都在vio 文件夾下面。

??

4、連接管理、連接線程模塊

連接管理模塊負(fù)責(zé)監(jiān)聽對MySQL Server 的各種請求,接收連接請求,轉(zhuǎn)發(fā)所有連接請求到線程管理模塊。每一個連接上MySQL Server 的客戶端請求都會被分配(或創(chuàng)建)一個連接線程為其單獨服務(wù)。而連接線程的主要工作就是負(fù)責(zé)MySQL Server 與客戶端的通信,接受客戶端的命令請求,傳遞Server 端的結(jié)果信息等。線程管理模塊則負(fù)責(zé)管理維護(hù)這些連接線程。包括線程的創(chuàng)建,線程的cache 等。

?

5、用戶模塊

用戶模塊所實現(xiàn)的功能,主要包括用戶的登錄連接權(quán)限控制和用戶的授權(quán)管理。他就像MySQL 的大門守衛(wèi)一樣,決定是否給來訪者“開門”。

?

6、Query 解析和轉(zhuǎn)發(fā)模塊

在MySQL 中我們習(xí)慣將所有Client端發(fā)送給Server 端的命令都稱為query,在MySQL Server 里面,連接線程接收到客戶端的一個Query 后,會直接將該query 傳遞給專門負(fù)責(zé)將各種Query 進(jìn)行分類然后轉(zhuǎn)發(fā)給各個對應(yīng)的處理模塊,這個模塊就是query 解析和轉(zhuǎn)發(fā)模塊。其主要工作就是將query 語句進(jìn)行語義和語法的分析,然后按照不同的操作類型進(jìn)行分類,然后做出針對性的轉(zhuǎn)發(fā)。

?

7、QueryCache 模塊

Query Cache 模塊在MySQL 中是一個非常重要的模塊,他的主要功能是將客戶端提交給MySQL 的Select 類query 請求的返回結(jié)果集cache 到內(nèi)存中,與該query 的一個hash 值做一個對應(yīng)。該Query 所取數(shù)據(jù)的基表發(fā)生任何數(shù)據(jù)的變化之后,MySQL 會自動使該query 的Cache 失效。在讀寫比例非常高的應(yīng)用系統(tǒng)中,Query Cache 對性能的提高是非常顯著的。當(dāng)然它對內(nèi)存的消耗也是非常大的。

?

8、日志記錄模塊

日志記錄模塊主要負(fù)責(zé)整個系統(tǒng)級別的邏輯層的日志的記錄,包括error log,binary log,slow query log 等。

?

9、Query 優(yōu)化器模塊

Query 優(yōu)化器,顧名思義,就是優(yōu)化客戶端請求的query,根據(jù)客戶端請求的query 語句,和數(shù)據(jù)庫中的一些統(tǒng)計信息,在一系列算法的基礎(chǔ)上進(jìn)行分析,得出一個最優(yōu)的策略,告訴后面的程序如何取得這個query 語句的結(jié)果。

?

10、表變更管理模塊

表變更管理模塊主要是負(fù)責(zé)完成一些DML 和DDL 的query,如:update,delte,insert,create table,alter table 等語句的處理。

?

11、表維護(hù)模塊

表的狀態(tài)檢查,錯誤修復(fù),以及優(yōu)化和分析等工作都是表維護(hù)模塊需要做的事情。

?

12、復(fù)制模塊

復(fù)制模塊又可分為Master 模塊和Slave 模塊兩部分, Master 模塊主要負(fù)責(zé)在Replication 環(huán)境中讀取Master 端的binary 日志,以及與Slave 端的I/O 線程交互等工作。

Slave 模塊比Master 模塊所要做的事情稍多一些,在系統(tǒng)中主要體現(xiàn)在兩個線程上面。一個是負(fù)責(zé)從Master請求和接受binary 日志,并寫入本地relay log 中的I/O 線程。另外一個是負(fù)責(zé)從relay log 中讀取相關(guān)日志事件,然后解析成可以在Slave 端正確執(zhí)行并得到和Master端完全相同的結(jié)果的命令并再交給Slave 執(zhí)行的SQL 線程。

?

13、系統(tǒng)狀態(tài)管理模塊

系統(tǒng)狀態(tài)管理模塊負(fù)責(zé)在客戶端請求系統(tǒng)狀態(tài)的時候,將各種狀態(tài)數(shù)據(jù)返回給用戶,像DBA 常用的各種showstatus 命令,showvariables 命令等,所得到的結(jié)果都是由這個模塊返回的。

?

14、訪問控制模塊

造訪客人進(jìn)門了就可以想干嘛就干嘛么?為了安全考慮,肯定不能如此隨意。這時候就需要訪問控制模塊實時監(jiān)控客人的每一個動作,給不同的客人以不同的權(quán)限。訪問控制模塊實現(xiàn)的功能就是根據(jù)用戶模塊中各用戶的授權(quán)信息,以及數(shù)據(jù)庫自身特有的各種約束,來控制用戶對數(shù)據(jù)的訪問。用戶模塊和訪問控制模塊兩者結(jié)合起來,組成了MySQL 整個數(shù)據(jù)庫系統(tǒng)的權(quán)限安全管理的功能。

?

15、表管理器

這個模塊從名字上看來很容易和上面的表變更和表維護(hù)模塊相混淆,但是其功能與變更及維護(hù)模塊卻完全不同。大家知道,每一個MySQL 的表都有一個表的定義文件,也就是*.frm文件。表管理器的工作主要就是維護(hù)這些文件,以及一個cache,該cache 中的主要內(nèi)容是各個表的結(jié)構(gòu)信息。此外它還維護(hù)table 級別的鎖管理。

?

16、存儲引擎接口模塊

存儲引擎接口模塊可以說是MySQL 數(shù)據(jù)庫中最有特色的一點了。目前各種數(shù)據(jù)庫產(chǎn)品中,基本上只有MySQL 可以實現(xiàn)其底層數(shù)據(jù)存儲引擎的插件式管理。這個模塊實際上只是一個抽象類,但正是因為它成功地將各種數(shù)據(jù)處理高度抽象化,才成就了今天MySQL 可插拔存儲引擎的特色。

?

17、核心API

核心API 模塊主要是為了提供一些需要非常高效的底層操作功能的優(yōu)化實現(xiàn),包括各種底層數(shù)據(jù)結(jié)構(gòu)的實現(xiàn),特殊算法的實現(xiàn),字符串處理,數(shù)字處理等,小文件I/O,格式化輸出,以及最重要的內(nèi)存管理部分。核心API 模塊的所有源代碼都集中在mysys和strings文件夾下面,有興趣的讀者可以研究研究。

?

?

存儲引擎

可以稱之為一種處理數(shù)據(jù)的能力,也可以稱之為是一種表的類型。

MyISAM: 擁有較高的插入,查詢速度,但不支持?事務(wù)

InnoDB?:5.5版本后Mysql的默認(rèn)數(shù)據(jù)庫,事務(wù)型數(shù)據(jù)庫的首選引擎,支持ACID事務(wù),支持行級鎖定

?

高并發(fā)下的mysql會發(fā)生什么問題?(以下文章僅僅討論InnoDB存儲引擎)

當(dāng)一個數(shù)據(jù)庫存在高并發(fā)的情況下,如果只進(jìn)行普通的查操作,那么我相信通過mysql本身的緩存機(jī)制和索引機(jī)制能夠達(dá)到很好的性能效果,但是如果還有其他讀寫操作呢?會發(fā)生什么事情?

一個經(jīng)典的例子是email,如果一個用戶在閱讀一封郵件的時候,同時另外一個用戶刪除了這一封郵件,最后會發(fā)生什么?結(jié)果是不確定的,有可能會報錯退出,有可能會讀取到不一樣的數(shù)據(jù)。

以此為基礎(chǔ),引入一個概念,?多版本并發(fā)控制(Multi-version concurrency control,MVCC),這個概念不僅僅是對于Mysql,包括Oracle、PostgreSQL等其他數(shù)據(jù)庫系統(tǒng)都實現(xiàn)了MVCC,只是各自的機(jī)制不同,它可以保證不阻塞地讀到一致的數(shù)據(jù),實現(xiàn)是通過保存數(shù)據(jù)在某個時間點的快照來實現(xiàn)的。再引入幾個基礎(chǔ)概念,?鎖、事務(wù)和隔離級別。

日志

MySQL Innodb中存在多種日志,除了錯誤日志、查詢?nèi)罩就?#xff0c;還有很多和數(shù)據(jù)持久性、一致性有關(guān)的日志。
bin.log是mysql服務(wù)層產(chǎn)生的日志,常用來進(jìn)行數(shù)據(jù)恢復(fù)、數(shù)據(jù)庫復(fù)制,常見的mysql主從架構(gòu),就是采用slave同步master的binlog實現(xiàn)的, 另外通過解析binlog能夠?qū)崿F(xiàn)mysql到其他數(shù)據(jù)源(如ElasticSearch)的數(shù)據(jù)復(fù)制。
redo.log記錄了數(shù)據(jù)操作在物理層面的修改,mysql中使用了大量緩存,緩存存在于內(nèi)存中,修改操作時會直接修改內(nèi)存,而不是立刻修改磁盤,當(dāng)內(nèi)存和磁盤的數(shù)據(jù)不一致時,稱內(nèi)存中的數(shù)據(jù)為臟頁(dirty page)。為了保證數(shù)據(jù)的安全性,事務(wù)進(jìn)行中時會不斷的產(chǎn)生redo log,在事務(wù)提交時進(jìn)行一次flush操作,保存到磁盤中, redo log是按照順序?qū)懭氲?#xff0c;磁盤的順序讀寫的速度遠(yuǎn)大于隨機(jī)讀寫。當(dāng)數(shù)據(jù)庫或主機(jī)失效重啟時,會根據(jù)redo log進(jìn)行數(shù)據(jù)的恢復(fù),如果redo log中有事務(wù)提交,則進(jìn)行事務(wù)提交修改數(shù)據(jù)。這樣實現(xiàn)了事務(wù)的原子性、一致性和持久性。
undo.Log除了記錄redo log外,當(dāng)進(jìn)行數(shù)據(jù)修改時還會記錄undo log,undo log用于數(shù)據(jù)的撤回操作,它記錄了修改的反向操作,比如,插入對應(yīng)刪除,修改對應(yīng)修改為原來的數(shù)據(jù),通過undo log可以實現(xiàn)事務(wù)回滾,并且可以根據(jù)undo log回溯到某個特定的版本的數(shù)據(jù),實現(xiàn)MVCC。
redo log 和binlog的一致性,為了防止寫完binlog但是redo log的事務(wù)還沒提交導(dǎo)致的不一致,innodb 使用了兩階段提交

一種提高共享資源并發(fā)性的操作就是讓鎖更有選擇性,盡量只鎖定需要修改的數(shù)據(jù)而不是所有數(shù)據(jù),但是加鎖也是需要消耗各種資源。鎖的各種操作,包括獲得鎖,檢查鎖是否解除,釋放鎖等,都會增加系統(tǒng)開銷,所以想要獲得最高性能的并發(fā)鎖策略,是在鎖的開銷和數(shù)據(jù)的安全性之間尋找一個最優(yōu)點。

鎖在架構(gòu)上分為兩層,一種為服務(wù)器層面的鎖,一種為存儲引擎層面的鎖。從功能上分為讀鎖和寫鎖(共享鎖和排它鎖)。

?

服務(wù)器層面的鎖是在執(zhí)行Alter Table的某些操作的時候,mysql服務(wù)器會忽略存儲引擎,直接會使用表鎖,執(zhí)行對應(yīng)的語句。

?

存儲引擎層面的鎖是為了最大程度的支持并發(fā)處理,在InnoDB,鎖分行鎖、Metadata Lock(事務(wù)級表鎖),行鎖的算法共有三種:Record Lock,Gap Lock,Next-Key Lock

?

Record Lock:單個行記錄的上鎖

Gap Lock:間歇鎖,不包含記錄本身的區(qū)間鎖

Next-Key Lock:包含記錄本身的區(qū)間鎖

只有在RR隔離級別下才會有g(shù)ap lock,next-key lock,其中

當(dāng)where條件為?普通索引時為gap lock或者Next-key Lock

當(dāng)where條件為?主鍵索引的時候,Next-key Lock 和Gap Lock的鎖策略降級為行鎖

當(dāng)where條件?不是索引的時候,innodb會給所有數(shù)據(jù)上鎖,然后返回Mysql server層,然后在Server層過濾掉不符合條件的數(shù)據(jù),通過調(diào)用?unlock_row方法解鎖

以下為測試間歇鎖的語句:

  • create table t(id int,name int,key idx_id(name),primary key(id))engine =innodb;

  • insert into t values(1,1),(3,3),(5,5),(8,8),(11,11);

  • session 1:select * from t where name=8 for update;

  • session 2:insert into t(id,name) values(12,6);

  • session 2:insert into t(id,name) values(6,6);

  • ?

    如何在事務(wù)中進(jìn)行行鎖操作?

    SELECT??語句中加??for???update??或者??lock???in???share??mode

    或者update、delete

    其中還有一種檢驗死鎖的算法叫做?wait-for graph?,沒當(dāng)請求沒有立即反應(yīng)的時候就會執(zhí)行。

    ?

    Metadate Lock主要解決了2個問題,一個是事務(wù)隔離問題,比如在可重復(fù)隔離級別下,會話A在2次查詢期間,會話B對表結(jié)構(gòu)做了修改,兩次查詢結(jié)果就會不一致,無法滿足可重復(fù)讀的要求;另外一個是數(shù)據(jù)復(fù)制的問題,比如會話A執(zhí)行了多條更新語句期間,另外一個會話B做了表結(jié)構(gòu)變更并且先提交,就會導(dǎo)致slave在重做時,先重做alter,再重做update時就會出現(xiàn)復(fù)制錯誤的現(xiàn)象。測試的時候可以在使用show processlist查看alter 表的session是否存在Waiting for table metadata lock狀態(tài)。

    ?

    事務(wù)

    我理解的事務(wù)為用戶和Mysql服務(wù)器完整的交流,從Start Transaction;開始后的所有sql語句直到commit;結(jié)束的一次數(shù)據(jù)交流,?一般mysql中會設(shè)置自動提交事務(wù)的特點,設(shè)置方法為?show variables like 'AUTOCOMMIT';

    ?

    事務(wù)應(yīng)該具有4個屬性:?原子性、一致性、隔離性、持久性。這四個屬性通常稱為ACID特性。

    ?

    原子性(atomicity)。一個事務(wù)是一個不可分割的工作單位,事務(wù)中包括的諸操作要么都做,要么都不做。
    一致性(consistency)。事務(wù)必須是使數(shù)據(jù)庫從一個一致性狀態(tài)變到另一個一致性狀態(tài)。一致性與原子性是密切相關(guān)的。

    隔離性(isolation)。一個事務(wù)的執(zhí)行不能被其他事務(wù)干擾。即一個事務(wù)內(nèi)部的操作及使用的數(shù)據(jù)對并發(fā)的其他事務(wù)是隔離的,并發(fā)執(zhí)行的各個事務(wù)之間不能互相干擾。

    持久性(durability)。持久性也稱永久性(permanence),指一個事務(wù)一旦提交,它對數(shù)據(jù)庫中數(shù)據(jù)的改變就應(yīng)該是永久性的。接下來的其他操作或故障不應(yīng)該對其有任何影響。

    ?

    其中原子性、持久性通過數(shù)據(jù)庫的?redo.log(重做日志)來實現(xiàn),?undo.log用來保證事務(wù)的一致性、隔離性。

    重做日志:每當(dāng)有操作執(zhí)行前,將數(shù)據(jù)真正更改時,先前相關(guān)操作寫入重做日志。這樣當(dāng)斷電,或者一些意外,導(dǎo)致后續(xù)任務(wù)無法完成時,系統(tǒng)恢復(fù)后,可以繼續(xù)完成這些更改
    撤消日志:當(dāng)一些更改在執(zhí)行一半時,發(fā)生意外,而無法完成,則可以根據(jù)撤消日志恢復(fù)到更改之前的壯態(tài)


    比如某一時刻數(shù)據(jù)庫宕機(jī)了,有兩個事務(wù),一個事務(wù)已經(jīng)提交,另一個事務(wù)正在處理
    數(shù)據(jù)庫重啟的時候就要根據(jù)日志進(jìn)行前滾及回退,把已提交事務(wù)的更改寫到數(shù)據(jù)文件,未提交事務(wù)的更改恢復(fù)到事務(wù)開始前的狀態(tài)。

    ?

    關(guān)于事務(wù),你可能不知道的地方:

    保存點(savepoint),當(dāng)開始一個事務(wù)的時候,里面會隱式的包含一個保存點,也可以在事務(wù)過程中使用保存點,當(dāng)RollBack的時候會回到上一個保存點的位置,每個保存點擁有一個ID。(?保存點只會遞增,例如:從3回滾到2,再記錄下一個保存點的時候ID為4)

    事務(wù)還可以進(jìn)行?鏈?zhǔn)聞?wù)(觸發(fā)器連接)、嵌套事務(wù)、分布式事務(wù)

    ?

    隔離級別

    未提交讀(?Read Uncommitted):允許臟讀,也就是可能讀取到其他會話中未提交事務(wù)修改的數(shù)據(jù),兩個事務(wù)互相透明。
    提交讀(?Read Committed):只能讀取到已經(jīng)提交的數(shù)據(jù),但是可能造成同一個事務(wù)中,由于另一個事務(wù)提前提交了一個更改事務(wù),導(dǎo)致select到的數(shù)據(jù)不一致,所以為不可重讀。
    可重復(fù)讀(?Repeatable Read):在同一個事務(wù)內(nèi)的查詢都是事務(wù)開始時刻一致的,InnoDB默認(rèn)級別。在SQL標(biāo)準(zhǔn)中,該隔離級別消除了不可重復(fù)讀,但是還存在幻讀。
    串行讀(?Serializable):完全串行化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞

    其他的都好理解,看一下什么是?幻讀:

    | session one ??? ? ? ? ? ? ???? ? ? ? ? ? ?? ?| ?session two?? ? ? ? ? ? ???? ? ? ? ? ? ??? ? ? ? ? ? ??|

    | begin?? ? ? ? ? ? ???? ? ? ? ? ? ?? ? ? ? ? ? ? ? | ?begin ? ?? ? ? ? ? ? ???? ? ? ? ? ? ???? ? ? ? ? ? ? ? ? ? |

    | select table ?where ?name ='join' ? | ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ?? ? ?? ? ?|

    | Empty set (0.00 sec)? ? ? ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |

    | ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | ?Insert into table (name) values ('join')? ? |

    | ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | ?Query OK, 1 row affected ? ? ? ? ? ? ? ? ? ?? |

    ?

    | select table ?where ?name ='join' ? | ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ?? ? ?? ? ?? ???|

    |?Empty set?(0.00 sec)? ? ? ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |

    | >update table set age=18 where ?| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|

    | >name='join' ; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|

    |?Query OK, 1 row affected? ??? ? ? ? ?| commit ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |

    | commit ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|

    圖中標(biāo)紅的地方,可以看到明明查不到數(shù)據(jù),但是卻update成功了,就和幻象一樣,幻讀之名由此而來。

    設(shè)置隔離級別

    1.查看?當(dāng)前會話隔離級別
    select @@tx_isolation;
    2.查看?系統(tǒng)當(dāng)前隔離級別
    select @@global.tx_isolation;
    3.設(shè)置?當(dāng)前會話隔離級別
    SET session TRANSACTION ISOLATION LEVEL repeatable read;
    4.設(shè)置?系統(tǒng)當(dāng)前隔離級別
    SET global TRANSACTION ISOLATION LEVEL repeatable read;

    ?

    ?

    InnoDB下的MVCC強版本控制:

    你可將MVCC看成行級別鎖的一種妥協(xié),它在許多情況下避免了使用鎖,同時可以提供更小的開銷。根據(jù)實現(xiàn)的不同,它可以

    允許非阻塞式讀,在寫操作進(jìn)行時只鎖定必要的記錄。

    在每一行數(shù)據(jù)中額外保存兩個隱藏的列:當(dāng)前行創(chuàng)建時的版本號和刪除時的版本號(可能為空)。這里的版本號并不是實際的時間值,?而是

    系統(tǒng)版本號。每開始個新的事務(wù),?系統(tǒng)版本號都會自動遞增。事務(wù)開始時刻的系統(tǒng)版本號會作為事務(wù)的版本號,用來和查詢每行記

    錄的版本號進(jìn)行比較。

    每個事務(wù)又有自己的版本號,這樣事務(wù)內(nèi)執(zhí)行CRUD操作時,就通過版本號的比較來達(dá)到數(shù)據(jù)版本控制的目的。

    InnoDB每行數(shù)據(jù)只在表中保留一份,在更新數(shù)據(jù)時上行鎖,同時將舊版數(shù)據(jù)寫入?undo log;表和 undo log 中行數(shù)據(jù)都記錄著事務(wù)ID,在檢索時,只讀取來自當(dāng)前已提交的

    事務(wù)的行數(shù)據(jù).

    ?

    MVCC具體的操作如下:
    ?

    SELECT:InnoDB會根據(jù)以下兩個條件檢查每行記錄:
    1)InnoDB只查找版本早于當(dāng)前事務(wù)版本的數(shù)據(jù)行(也就是,行的系統(tǒng)版本號小于或等于事務(wù)的系統(tǒng)版本號),這樣可以確保事務(wù)讀取的行,只么是在事務(wù)開始前已經(jīng)存在的,要么是事務(wù)自身插入或者修改過的。
    2)行的刪除版本要么未定義,要么大于當(dāng)前事務(wù)版本號。這可以確保事務(wù)讀取到的行,在事務(wù)開始之前未被刪除。
    INSERT:InnoDB為新插入的每一行保存當(dāng)前系統(tǒng)版本號作為行版本號。
    DELETE:InnoDB為刪除的每一行保存當(dāng)前系統(tǒng)版本號作為行刪除標(biāo)識。
    UPDATE:InnoDB為插入一行新記錄,保存當(dāng)前系統(tǒng)版本號作為行版本號,同時保存當(dāng)系統(tǒng)的版本號為原來的行作為刪除標(biāo)識。
    ?

    保存這兩個額外系統(tǒng)版本號,使大多數(shù)操作都可以不用加鎖。這樣設(shè)計使得計數(shù)據(jù)操作很簡單,性能很好,并且也能保證只會讀取到符合標(biāo)準(zhǔn)的行。不足之處是每行記錄都需要額外的存儲空間,需要做更多的行檢查工作,以及一些額外的維護(hù)工作。
    MVCC只在REPEATABLE READ和READ COMMITED兩個隔離級別下工作,其它兩個隔離級別和MVCC不兼容。

    可是為什么RR級別和RC級別看到的數(shù)據(jù)不一樣呢?我們來看看innodb中MVCC的具體原理是怎么處理的

    隱藏列

    在分析MVCC原理之前,先看下InnoDB中數(shù)據(jù)行的結(jié)構(gòu):

    在InnoDB中,每一行都有2個隱藏列?DATA_TRX_ID和?DATA_ROLL_PTR(如果沒有定義主鍵,則還有個隱藏主鍵列):
    DATA_TRX_ID表示最近修改該行數(shù)據(jù)的事務(wù)ID
    DATA_ROLL_PTR則表示指向該行回滾段的指針,該行上所有舊的版本,在undo中都通過鏈表的形式組織,而該值,正式指向undo中該行的歷史記錄鏈表
    整個MVCC的關(guān)鍵就是通過DATA_TRX_ID和DATA_ROLL_PTR這兩個隱藏列來實現(xiàn)的。

    事務(wù)鏈表

    MySQL中的事務(wù)在開始到提交這段過程中,都會被保存到一個叫?trx_sys的事務(wù)鏈表中,這是一個基本的鏈表結(jié)構(gòu):

    事務(wù)鏈表中保存的都是還未提交的事務(wù),事務(wù)一旦被提交,則會被從事務(wù)鏈表中摘除。

    Read View

    有了前面隱藏列和事務(wù)鏈表的基礎(chǔ),接下去就可以構(gòu)造MySQL實現(xiàn)MVCC的關(guān)鍵——ReadView。
    ReadView說白了就是一個數(shù)據(jù)結(jié)構(gòu),在SQL開始的時候被創(chuàng)建。這個數(shù)據(jù)結(jié)構(gòu)中包含了3個主要的成員:?ReadView{low_trx_id, up_trx_id, trx_ids},在并發(fā)情況下,一個事務(wù)在啟動時,trx_sys鏈表中存在部分還未提交的事務(wù),那么哪些改變對當(dāng)前事務(wù)是可見的,哪些又是不可見的,這個需要通過ReadView來進(jìn)行判定,首先來看下ReadView中的3個成員各自代表的意思:
    low_trx_id表示該事務(wù)啟動時,當(dāng)前事務(wù)鏈表中最大的事務(wù)id編號,也就是最近創(chuàng)建的除自身以外最大事務(wù)編號;
    up_trx_id表示該事務(wù)啟動時,當(dāng)前事務(wù)鏈表中最小的事務(wù)id編號,也就是當(dāng)前系統(tǒng)中創(chuàng)建最早但還未提交的事務(wù);
    trx_ids表示所有事務(wù)鏈表中事務(wù)的id集合。
    上述3個成員組成了ReadView中的主要部分,簡單圖示如下:

    根據(jù)上圖所示,所有數(shù)據(jù)行上DATA_TRX_ID小于up_trx_id的記錄,說明修改該行的事務(wù)在當(dāng)前事務(wù)開啟之前都已經(jīng)提交完成,所以對當(dāng)前事務(wù)來說,都是可見的。而對于DATA_TRX_ID大于low_trx_id的記錄,說明修改該行記錄的事務(wù)在當(dāng)前事務(wù)之后,所以對于當(dāng)前事務(wù)來說是不可見的。
    注意,?ReadView是與SQL綁定的,而并不是事務(wù),所以即使在同一個事務(wù)中,每次SQL啟動時構(gòu)造的ReadView的up_trx_id和low_trx_id也都是不一樣的,至于DATA_TRX_ID大于low_trx_id本身出現(xiàn)也只有當(dāng)多個SQL并發(fā)的時候,在一個SQL構(gòu)造完ReadView之后,另外一個SQL修改了數(shù)據(jù)后又進(jìn)行了提交,對于這種情況,數(shù)據(jù)其實是不可見的。
    最后,至于位于(up_trx_id, low_trx_id)中間的事務(wù)是否可見,這個需要根據(jù)?不同的事務(wù)隔離級別來確定。對于RC的事務(wù)隔離級別來說,對于事務(wù)執(zhí)行過程中,已經(jīng)提交的事務(wù)的數(shù)據(jù),對當(dāng)前事務(wù)是可見的,也就是說上述圖中,當(dāng)前事務(wù)運行過程中,trx1~4中任意一個事務(wù)提交,對當(dāng)前事務(wù)來說都是可見的;而對于RR隔離級別來說,事務(wù)啟動時,已經(jīng)開始的事務(wù)鏈表中的事務(wù)的所有修改都是不可見的,所以在RR級別下,low_trx_id基本保持與up_trx_id相同的值即可。這里解釋完也可以了解為什么會出現(xiàn)幻讀。

    名詞解釋

    DML(Data Manipulation Language)數(shù)據(jù)操縱語言:

    適用范圍:對數(shù)據(jù)庫中的數(shù)據(jù)進(jìn)行一些簡單操作,如insert,delete,update,select等.

    DDL(Data Definition Language)數(shù)據(jù)定義語言:

    適用范圍:對數(shù)據(jù)庫中的某些對象(例如,database,table)進(jìn)行管理,如Create,Alter和Drop.

    來源:https://blog.csdn.net/liqinglin06/article/details/77750246

    總結(jié)

    以上是生活随笔為你收集整理的Innodb隔离级别的实现原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。