前言
上一篇文章咱們介紹了MySQL 的四大事務特性 ACID,以及innodb 的事務隔離級別RU,RC,RR,可串行化,在結尾的時候我還賣了個關子,讓你們思考一下 innodb 的事務隔離級別在 MySQL 中是如何實現的,不知道你們思考得咋樣了,anyway,咱們今天就在這裏繼續講關於事務隔離級別的實現方案。php
老規矩,先上飛機票:程序員
- MySQL相關(一)- 一條查詢語句是如何執行的
- MySQL相關(二)- 一條更新語句是如何執行的
- MySQL相關(番外篇)- innodb 邏輯存儲結構;
- MySQL相關(三)- 索引數據模型推演及 B+Tree 的詳細介紹;
- MySQL相關(四)- 性能優化關鍵點索引
- 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 核心知識解析,職業生涯,面試總結等文章會不按期堅持推送輸出,歡迎你們關注~~~