MySQL相關(六)- 事務隔離級別的實現方案(MVCC)

前言

上一篇文章咱們介紹了MySQL 的四大事務特性 ACID,以及innodb 的事務隔離級別RU,RC,RR,可串行化,在結尾的時候我還賣了個關子,讓你們思考一下 innodb 的事務隔離級別在 MySQL 中是如何實現的,不知道你們思考得咋樣了,anyway,咱們今天就在這裏繼續講關於事務隔離級別的實現方案。php

老規矩,先上飛機票:程序員

  1. MySQL相關(一)- 一條查詢語句是如何執行的
  2. MySQL相關(二)- 一條更新語句是如何執行的
  3. MySQL相關(番外篇)- innodb 邏輯存儲結構
  4. MySQL相關(三)- 索引數據模型推演及 B+Tree 的詳細介紹
  5. MySQL相關(四)- 性能優化關鍵點索引
  6. MySQL相關(五)- 事務特性及隔離級別的詳細介紹

前面提到的腦圖以下,想要完整高清圖片能夠到微信個人公衆號下【6曦軒】下回復 MySQL 腦圖獲取: 在這裏插入圖片描述面試

正文

正文中的絮絮不休

咱們你們先思考一下,若是要解決讀一致性的問題,保證一個事務中先後兩次讀取數據結果一致,實現事務隔離,應該怎麼作?咱們有哪一些方法呢?你的思路是什麼樣的呢?數據庫

整體上來講,咱們有兩大類的方案。性能優化

LBCC

第一種,我既然要保證先後兩次讀取數據一致,那麼我讀取數據的時候,鎖定我要操做的數據,不容許其餘的事務修改就好了。這種方案咱們叫作基於鎖的併發控制 Lock Based Concurrency Control(LBCC)。微信

若是僅僅是基於鎖來實現事務隔離,一個事務讀取的時候不容許其餘時候修改,那就意味着不支持併發的讀寫操做,而咱們的大多數應用都是讀多寫少的,這樣會極大地影響操做數據的效率。架構

MVCC

因此咱們還有另外一種解決方案,若是要讓一個事務先後兩次讀取的數據保持一致,那麼咱們能夠在修改數據的時候給它創建一個備份或者叫快照,後面再來讀取這個快照就好了。這種方案咱們叫作多版本的併發控制 Multi Version Concurrency Control (MVCC)。併發

MVCC 的核心思想是: 我能夠查到在我這個事務開始以前已經存在的數據,即便它在後面被修改或者刪除了。在我這個事務以後新增的數據,我是查不到的。mvc

  • 問題:這個快照何時建立?讀取數據的時候,怎麼保證能讀取到這個快照而不是最新的數據?這個怎麼實現呢?

InnoDB 爲每行記錄都實現了兩個隱藏字段: <br />DB_TRX_ID,6 字節:插入或更新行的最後一個事務的事務 ID,事務編號是自動遞增的(咱們把它理解爲建立版本號,在數據新增或者修改成新數據的時候,記錄當前事務 ID)。 <br />DB_ROLL_PTR,7 字節:回滾指針(咱們把它理解爲刪除版本號,數據被刪除或記錄爲舊數據的時候,記錄當前事務 ID)。 <br />咱們把這兩個事務 ID 理解爲版本號。 在這裏插入圖片描述性能

第一個事務,初始化數據(檢查初始數據)

//Transaction 1

begin;

insert into mvcctest values(NULL,'Jerry') ;

insert into mvcctest values(NULL,'jack') ;

commit;

此時的數據,建立版本是當前事務 ID,刪除版本爲空:

id name 建立版本 刪除版本
1 Jerry 1 undefined
2 jack 1 undefined

第二個事務,執行第 1 次查詢,讀取到兩條原始數據,這個時候事務 ID 是 2:

// Transaction 2
begin;
select * from mvcctest ;	-- (1) 第一次查詢

第三個事務,插入數據:

// Transaction 3
begin;
insert into mvcctest values(NULL,'tom') ;
commit;

此時的數據,多了一條 tom,它的建立版本號是當前事務編號,3:

id name 建立版本 刪除版本
1 Jerry 1 undefined
2 jack 1 undefined
3 tom 3 undefined

第二個事務,執行第 2 次查詢:

Transaction 2
select * from mvcctest ;	(2) 第二次查詢

MVCC 的查找規則:只能查找建立時間小於等於當前事務 ID 的數據,和刪除時間大於當前事務 ID 的行(或未刪除)。

也就是不能查到在個人事務開始以後插入的數據,tom 的建立 ID 大於 2,因此仍是隻能查到兩條數據。

第四個事務,刪除數據,刪除了 id=2 jack 這條記錄:

Transaction 4
begin;
delete from mvcctest where id=2;
commit;

此時的數據,jack 的刪除版本被記錄爲當前事務 ID,4,其餘數據不變:

id name 建立版本 刪除版本
1 Jerry 1 undefined
2 jack 1 4
3 tom 3 undefined

在第二個事務中,執行第 3 次查詢:

Transaction 2
select * from mvcctest ;	(3) 第三次查詢

查找規則:只能查找建立時間小於等於當前事務 ID 的數據,和刪除時間大於當前事務 ID 的行(或未刪除)。

也就是,在我事務開始以後刪除的數據,因此 jack 依然能夠查出來。因此仍是這兩條數據。

第五個事務,執行更新操做,這個事務事務 ID 是 5:

Transaction 4
begin;
update mvcctest set name ='Mic' where id=1;
commit;

此時的數據,更新數據的時候,舊數據的刪除版本被記錄爲當前事務 ID 5(undo),產生了一條新數據,建立 ID 爲當前事務 ID 5:

id name 建立版本 刪除版本
1 Jerry 1 5
2 jack 1 4
3 tom 3 undefined
1 Mic 5 undefined

第二個事務,執行第 4 次查詢:

// Transaction 2
select * from mvcctest ;	(4) 第四次查詢

查找規則:只能查找建立時間小於等於當前事務 ID 的數據,和刪除時間大於當前事務 ID 的行(或未刪除)。

由於更新後的數據 Mic 建立版本大於 2,表明是在事務以後增長的,查不出來。

而舊數據 Jerry 的刪除版本大於 2,表明是在事務以後刪除的,能夠查出來。

經過以上演示咱們能看到,經過版本號的控制,不管其餘事務是插入、修改、刪除,第一個事務查詢到的數據都沒有變化。

在 InnoDB 中,MVCC 是經過 Undo log 實現的。

Oracle、Postgres 等等其餘數據庫都有 MVCC 的實現。

須要注意,在 InnoDB 中,MVCC 和鎖是協同使用的,這兩種方案並非互斥的。第一大類解決方案是鎖,鎖又是怎麼實現讀一致性的呢?

關於鎖的知識已經在馬不停蹄準備了,且聽下回分解~

By the way

有問題?能夠給我留言或私聊 有收穫?那就順手點個讚唄~

固然,也能夠到個人公衆號下「6曦軒」,

回覆「學習」,便可領取一份 【Java工程師進階架構師的視頻教程】~

回覆「面試」,能夠得到: 【本人嘔心瀝血整理的 Java 面試題】

回覆「MySQL腦圖」,能夠得到 【MySQL 知識點梳理高清腦圖】

因爲我咧,科班出身的程序員,php,Android以及硬件方面都作過,不過最後仍是選擇專一於作 Java,因此有啥問題能夠到公衆號提問討論(技術情感傾訴均可以哈哈哈),看到的話會盡快回復,但願能夠跟你們共同窗習進步,關於服務端架構,Java 核心知識解析,職業生涯,面試總結等文章會不按期堅持推送輸出,歡迎你們關注~~~

在這裏插入圖片描述

相關文章
相關標籤/搜索