什么是 MySQL InnoDB 的 MVCC?
MVCC
(Multi-Version Concurrency Control)是一種基于多版本的并發(fā)控制協(xié)議,只有在 InnoDB 引擎下存在。MVCC 是為了實現(xiàn)事務(wù)的隔離性,即通過版本號,避免同一數(shù)據(jù)在不同事務(wù)間的競爭,可以把它當(dāng)成基于多版本號的一種樂觀鎖。當(dāng)然,這種樂觀鎖只在事務(wù)級別讀已提交(RC)和可重復(fù)讀(RR)有效。MVCC 最大的好處,讀不加鎖,讀寫不沖突。在讀多寫少的應(yīng)用中,讀寫不沖突是非常重要的,極大的增加了系統(tǒng)的并發(fā)性能。
前置了解知識:
MySQL 的核心日志有哪些?
MySQL 中有七種日志文件,分別是:redo log(重做日志)、undo log(回滾日志)、bin log(二進制日志)、error log(錯誤日志)、slow query log(慢查詢?nèi)罩荆?、general log(一般查詢?nèi)罩荆瑀elay log(中繼日志)
bin log: 就是 binary log,二進制日志文件,記錄了 MySQL 所有的 DDL 和 DML (除了數(shù)據(jù)查詢語句)操作,以事件形式記錄,還包含語句所執(zhí)行的消耗的時間,MySQL 的二進制日志是事務(wù)安全型的。通過 bin log 日志我們可以做數(shù)據(jù)恢復(fù),增量備份,主主復(fù)制和主從復(fù)制等等
undo log: 是 MySQL 用來記錄事務(wù)操作的 反方向邏輯日志
,顧名思義,undo log是一種用于撤銷回退的日志,在事務(wù)沒提交之前,MySQL 會先記錄更新前的數(shù)據(jù)到 undo log 日志文件里面,當(dāng)事務(wù)回滾時或者數(shù)據(jù)庫崩潰時,可以利用 undo log 來進行回退
redo log: 是 InnoDB 存儲引擎產(chǎn)生的,記錄事務(wù)對數(shù)據(jù)頁的修改,如果 mysql 掛了,重啟后 InnoDB 會使用redo log 恢復(fù)數(shù)據(jù),保證了數(shù)據(jù)的持久性
數(shù)據(jù)庫的事務(wù)特性 (ACID)有哪些?
-
原子性(Atomicity):事務(wù)中的全部操作在數(shù)據(jù)庫中是不可分割的,要么全部完成,要么全部不執(zhí)行
-
持久性(Durability): 對于任意已提交事務(wù),系統(tǒng)必須保證該事務(wù)對數(shù)據(jù)庫的改變不被丟失,即使數(shù)據(jù)庫出現(xiàn)故障
-
隔離性(Isolation):事務(wù)的執(zhí)行不受其他事務(wù)的干擾,事務(wù)執(zhí)行的中間結(jié)果對其他事務(wù)必須是透明的
-
一致性(Consistency):幾個并行執(zhí)行的事務(wù),其執(zhí)行結(jié)果必須與按某一順序串行執(zhí)行的結(jié)果相一致
MySQL 數(shù)據(jù)庫的隔離級別有哪些,都會產(chǎn)生什么樣的問題?
臟讀 |
臟讀指的是讀到了其他事務(wù)未提交的數(shù)據(jù),未提交意味著這些數(shù)據(jù)可能會回滾,也就是可能最終不會存到數(shù)據(jù)庫中,也就是不存在的數(shù)據(jù)。讀到了并一定最終存在的數(shù)據(jù) |
不可重復(fù)讀 |
不可重復(fù)讀指的是在一個事務(wù)內(nèi),最開始讀到的數(shù)據(jù)和事務(wù)結(jié)束前的任意時刻讀到的同一批數(shù)據(jù)出現(xiàn)不一致的情況。 |
幻讀 |
同樣一筆查詢在整個事務(wù)過程中多次執(zhí)行后,查詢所得的結(jié)果集是不一樣的。也是指當(dāng)事務(wù)不獨立執(zhí)行時,插入或者刪除另一個事務(wù)當(dāng)前影響的數(shù)據(jù)而發(fā)生的一種類似幻覺的現(xiàn)象。 |
什么是 MySQL InnoDB 當(dāng)前讀、快照讀?
什么是當(dāng)前讀?
它讀取的數(shù)據(jù)庫記錄,都是當(dāng)前最新的版本,會對當(dāng)前讀取的數(shù)據(jù)進行加鎖,防止其他事務(wù)修改數(shù)據(jù)。是悲觀鎖的一種操作。
如下操作都是當(dāng)前讀:
-
select lock in share mode(共享鎖)
-
select for update(排他鎖)
-
update(排他鎖)
-
insert(排他鎖)
-
delete(排他鎖)
-
串行化事務(wù)隔離級別
什么是快照讀?
快照讀的實現(xiàn)是基于多版本并發(fā)控制,即 MVCC,既然是多版本,那么快照讀到的數(shù)據(jù)不一定是當(dāng)前最新的數(shù)據(jù),有可能是之前歷史版本的數(shù)據(jù)。普通的 select 查詢語句(即不加鎖的 select 操作)就是快照讀
重要:后續(xù)圖解會進行演示
Read Committed 隔離級別:每次 select 都生成一個快照讀。
Read Repeatable 隔離級別:開啟事務(wù)后第一個 select 語句才是快照讀的地方,而不是一開啟事務(wù)就快照讀。即僅在第一次執(zhí)行快照讀時生成
為什么需要 MVCC?
-
讀寫鎖的出現(xiàn)
讀鎖和讀鎖之間不互斥,而寫鎖和寫鎖、讀鎖都互斥。這樣就很大提升了系統(tǒng)的并發(fā)能力。之后人們發(fā)現(xiàn)并發(fā)讀還是不夠
-
MVCC 概念出現(xiàn)
能不能讓讀寫之間也不沖突的方法,就是讀取數(shù)據(jù)時通過一種類似快照的方式將數(shù)據(jù)保存下來,這樣讀鎖就和寫鎖不沖突了,不同的事務(wù) session 會看到自己特定版本的數(shù)據(jù)。當(dāng)然快照是一種概念模型,不同的數(shù)據(jù)庫可能用不同的方式來實現(xiàn)這種功能
總結(jié):MVCC
最大的優(yōu)點是讀不加鎖,因此讀寫不沖突,并發(fā)性能好,類比 Java 中的讀寫鎖,它是會存在讀寫競爭的,會有這個性能問題。MVCC 在 MySQL InnoDB 中的實現(xiàn)主要是為了提高數(shù)據(jù)庫并發(fā)性能,用更好的方式去處理讀-寫沖突,做到即使有讀寫沖突時,也能做到不加鎖,非阻塞并發(fā)讀
MVCC 實現(xiàn)的原理
MySQL MVCC 實現(xiàn)原理:
具體實現(xiàn)主要是由 隱式字段 + undolog 版本鏈 + read view 的方式實現(xiàn)
隱式字段:
-
DB_TRX_ID
:最近修改(修改/插入
)事務(wù)ID:記錄創(chuàng)建這條記錄/最后一次修改該記錄的事務(wù) ID,這個 id 是遞增的 -
DB_ROLL_PTR
:回滾指針,指向這條記錄的上一個版本
圖解:
id |
name |
|
|
1088 |
張三 |
1 |
0x1232123 |
undolog 版本鏈?zhǔn)鞘裁矗?/h4>
在每次更新該記錄后,都會將舊值放到一條 undo 日志中。隨著更新次數(shù)的增多,所有的版本都會被 roll_pointer 屬性連接成一條鏈表,這個鏈表就稱之為版本鏈。
圖解:
Read View 是什么?
原文:
read view
An internal snapshot used by the MVCC mechanism of InnoDB. Certain transactions, depending on their isolation level, see the data values as they were at the time the transaction (or in some cases, the statement) started. Isolation levels that use a read view are REPEATABLE READ, READ COMMITTED, and READ UNCOMMITTED.
Read View是一個數(shù)據(jù)庫的內(nèi)部快照,該快照被用于 InnoDB 存儲引擎中的 MVCC 機制。簡單點說,Read View 就是一個快照,保存著數(shù)據(jù)庫某個時刻的數(shù)據(jù)信息。Read View 會根據(jù)事務(wù)的隔離級別決定在某個事務(wù)開始時,該事務(wù)能看到什么信息。就是說通過 Read View,事務(wù)可以知道此時此刻能看到哪個版本的數(shù)據(jù)記錄(有可能不是最新版本的,也有可能是最新版本的)??芍貜?fù)讀、讀已提交、讀未提交,這幾個隔離級別都會使用 Read View
通俗點我的理解,Readview 是一個數(shù)據(jù)結(jié)構(gòu),包含四個字段,Read View 是"快照讀" SQL 執(zhí)行時 MVCC 提取數(shù)據(jù)的依據(jù)和規(guī)則
ReadView 包含的內(nèi)容:
-
m_ids:當(dāng)前活躍的事務(wù)編號集合,即還未提交。
-
min_trx_id:最小活躍事務(wù)編號
-
max_trx_id:預(yù)分配事務(wù)編號,當(dāng)前最大事務(wù)編號 +1
-
creator_trx_id: ReadView 創(chuàng)建者的事務(wù)編號
ReadView 提取數(shù)據(jù)得規(guī)則:
1、被訪問的 trx_id 與 readview 中的 creator_trx_id 相同,表示當(dāng)前事務(wù)在訪問自己修改的記錄,可見,返回;
2、被訪問的 trx_id 小于 min_trx_id,表明該版本已提交,可見,返回;
3、被訪問的 trx_id 大于等于 max_trx_id ,表明該版本在生成 readview 時,還未開啟,不可見,返回;
4、被訪問的 trx_id 在 min_trx_id 和 max_trx_id 之間,判斷是否在 m_ids 中,如果在,則說明生成 readview時,該版本事務(wù)未提交,該版本不可見;如果不在,則說明生成 readview 時,該版本事務(wù)已提交可見,返回。
案例圖解說明:
思考:RC、RR 兩種隔離級別下,兩次查詢的結(jié)果是什么?
答案:
RR 級別: select1="張三" select2="張三"
RC 級別: select1="張三" select2="張小三" RC 隔離級別下出現(xiàn)了"不可重復(fù)讀",后續(xù)講解為什么
上述問題的產(chǎn)生以及 ReadView 解析:
RC 級別下得 read view 圖解分析
1. 這是第兩次我們進行 select 語句 read view 給我們生成的:
?2. 第一次 select 語句分析圖解:
?
3. 第二次 select 語句分析圖解:
?
4. 產(chǎn)生的問題:
在 RC 隔離級別下,兩次 select 語句讀取到的內(nèi)容不一致問題,即證明了 MySQL 數(shù)據(jù)庫的隔離級為 RC 級別下會產(chǎn)生不可重復(fù)讀問題,再次證實前置了解知識模塊中的不同隔離級別產(chǎn)生的問題所述。原因是 READ COMMITTD 在每一次進行普通 SELECT 操作前都會生成一個 ReadView
RR 級別下得 read view 圖解分析
?
1. 連續(xù)多次快照讀,ReadView 會產(chǎn)生復(fù)用,沒有幻讀問題
2. RR 級別下使用 MVCC 能避免幻讀嗎?
答案:能,但不完全能!
特例:當(dāng)兩次快照讀之間存在當(dāng)前讀,ReadView會重新生成,導(dǎo)致產(chǎn)生幻讀
MVCC 總結(jié):
所謂的MVCC(Multi-Version Concurrency Control ,多版本并發(fā)控制)
指的就是在使用讀已提交(READ COMMITTD)、可重復(fù)讀(REPEATABLE READ)
這兩種隔離級別的事務(wù)在執(zhí)行普通的 SELECT 操作時訪問記錄的版本鏈的過程,這樣子可以使不同事務(wù)的讀-寫、寫-讀操作并發(fā)執(zhí)行,從而提升系統(tǒng)性能。
這兩個隔離級別的一個很大不同就是:生成ReadView的時機不同
,READ COMMITTD 在每一次進行普通 SELECT操作前都會生成一個 ReadView,而 REPEATABLE READ 只在第一次進行普通 SELECT 操作前生成一個ReadView,數(shù)據(jù)的可重復(fù)讀其實就是 ReadView 的重復(fù)使用。文章來源:http://www.zghlxwxcb.cn/news/detail-835249.html
擴展知識:
1. "當(dāng)前讀" 的實現(xiàn)是基于 next-key lock (行記錄鎖+Gap間隙鎖)文章來源地址http://www.zghlxwxcb.cn/news/detail-835249.html
到了這里,關(guān)于《MySQL MVCC 》的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!