MYSQL數(shù)據(jù)庫Innodb?引擎mvcc鎖實現(xiàn)原理
前言:
大家都知道在java 開發(fā)過程中,會經(jīng)常用到鎖,在java 代碼中,我們都知道鎖是加在對象頭上的,在java對象布局中有鎖的標志位。程序通過判斷鎖的標志位來獲取加鎖的情況。但是在mysql 中,鎖的實現(xiàn)原理是什么呢??赡艽蠹叶悸犨^ mvcc,但是mvcc 的實現(xiàn)原理是什么呢,可能就說不太清楚了,本文就以實例說明來mvcc 的實現(xiàn)原理。
1 數(shù)據(jù)庫設(shè)置隔離級別
我們都知道數(shù)據(jù)庫的隔離級別可以分為以下:
- 讀未提交 RU : read uncommitted
- 讀提交 RC : read committed
- 可重復(fù)讀 RR : repeatable read 默認隔離級別
- 序列化 SE : serializable 序列化
我們使用select @@transaction_isolation;來查看數(shù)據(jù)庫的隔離級別,可能有讀者會有疑問了,不是應(yīng)該是select @@tx_isolation;嗎, 因為我的電腦上裝的是 mysql 8.0, 之前的 tx 也是 transaction的簡寫,高版本的mysql 已經(jīng)使用 transaction 了。
# 查詢當(dāng)前會話隔離級別 select @@transaction_isolation; # 查看當(dāng)前系統(tǒng)的隔離級別 select @@global.transaction_isolation; # 命令行開啟事務(wù),區(qū)別在于前者是第一條語句的執(zhí)行時間點為事務(wù)開始的時間點,建立一致性讀,后者是立即建立一致性讀,開始執(zhí)行事務(wù) start transaction 或者 start transaction with consistent snapshot # 設(shè)置數(shù)據(jù)庫隔離級別,如若設(shè)置會話級別,則修改 global 為session 即可 set global transaction isolation level REPEATABLE READ; set global transaction isolation level READ COMMITTED; set global transaction isolation level READ UNCOMMITTED; set global transaction isolation level SERIALIZABLE;
2 數(shù)據(jù)庫表以及案例操作
操作的數(shù)據(jù)庫表為:
CREATE TABLE `t_user` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主鍵', `age` int DEFAULT NULL COMMENT '年齡', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用戶表'; # 初始化語句 INSERT INTO `t_user`(`id`, `age`) VALUES (1, 3); # 使用的操作語句 select * from t_user where id = 1; update t_user set age = age + 1 where id = 1;
案例如圖所示:

sessionA,sessionB,sessionC 是連接數(shù)據(jù)庫的3個會話窗口,mysql 數(shù)據(jù)庫的默認隔離級別為RR,為了達到效果我們把當(dāng)前的數(shù)據(jù)庫隔離級別改為RC,語句如下:
set session transaction isolation level READ COMMITTED;
setp1 準備工作,設(shè)置當(dāng)前會話事務(wù)隔離級別,查看數(shù)據(jù)庫表中的數(shù)據(jù)。窗口從左往右依次是sessionA、sessinB和sessionC。

setp2 t1 時刻,sessionA 和 sessionB 開啟事務(wù)。sessionA t2 時刻執(zhí)行查詢語句,sessionC 執(zhí)行更新操作,可以看到此時 sessionC 立馬執(zhí)行成功。

step3 t3 時刻,sessionB 查詢最新的數(shù)據(jù),并執(zhí)行更新操作

step4 t4 時刻,sessionA 查詢最新數(shù)據(jù),并在t5 時刻更新數(shù)據(jù),這里因為sessionB 的事務(wù)還未結(jié)束,因此執(zhí)行的操作會阻塞到超時(這里刻意等待到超時),t6 時刻sessionB 查詢數(shù)據(jù),并提交事務(wù)。

step5 sesionB 提交事務(wù)后,sessionA 重新執(zhí)行語句,可以看到執(zhí)行后的結(jié)果,然后提交事務(wù)。

通過上述的操作案例,我們可以觀察到:
- 在一個事務(wù)中的數(shù)據(jù)更改對本事務(wù)是可見的,此外的事務(wù)是否可見取決于其隔離級別。
- 兩個事務(wù)操作同一條數(shù)據(jù),會造成等待。innodb 引擎默認開啟死鎖檢測,并會設(shè)置鎖的超時時間。
# 設(shè)置超時時間默認是50s innodb_lock_wait_timeout # 開啟死鎖檢測,默認是開啟死鎖檢測,默認值為 on,開啟死鎖檢測 innodb_deadlock_detect # 查看更多innodb 引擎的配置 show variables like '%innodb%'
- 在會話中進行數(shù)據(jù)修改,默認事務(wù)是自動提交的。
3 mvcc 實現(xiàn)原理
mvcc,Multi-Version Concurrency Control,即版本并發(fā)訪問控制,是 mysql innodb 引擎實現(xiàn)的一種并發(fā)訪問控制方法,用于其事務(wù)的實現(xiàn)。其主要作用是為了提高數(shù)據(jù)庫的并發(fā)性能,更好地處理讀寫沖突問題,在遇到?jīng)_突的境況也能夠做到不加鎖,非阻塞并發(fā)讀取數(shù)據(jù)。 在解釋實現(xiàn)原理前,先了解一下當(dāng)前讀和快照讀。
- 當(dāng)前讀:像更改數(shù)據(jù)的操作(update/delete/insert)操作還有共享鎖和排他鎖(lock in share mode和for update)這操作都需要讀取當(dāng)前最新版本的數(shù)據(jù),否則就會有更新丟失的情況。數(shù)據(jù)庫的修改操作也是將數(shù)據(jù)所在的數(shù)據(jù)頁從磁盤加載到內(nèi)存,然后進行修改,最后寫回到磁盤中。
- 快照讀:不加鎖的select獲取數(shù)據(jù)就是讀快照,不會產(chǎn)生阻塞。在串行化的數(shù)據(jù)庫隔離級別下,快照讀會退化成當(dāng)前度。每行數(shù)據(jù)都有多個版本,當(dāng)前讀就是獲取最新的已經(jīng)提交過得版本數(shù)據(jù)。
接下來講述的是非常重要的部分:
- 已經(jīng)提交的事務(wù)
- 未被提交的事務(wù)
- 未開始的事務(wù)
在可重復(fù)讀的隔離級別下,事務(wù)啟動需要對整個庫做備份,這顯然是不可能的,實際上mvcc也沒有這樣做,而是利用版本號來控制,也就是事務(wù)開始時向 innodb 事務(wù)系統(tǒng)申請一個 transaction id,這個id申請順序是嚴格遞增的。每行數(shù)據(jù)的版本號 row trx_id = transaction id , 也就可以保證其版本的唯一性??梢员WC事務(wù)啟動時,會獲取當(dāng)前所有活躍事務(wù)id的數(shù)組,可以保證其唯一性。事務(wù)id數(shù)組中的最小的id記為低水位,數(shù)組中的最大id+1記為高水位。低水位和高水位便是已經(jīng)提交的事務(wù)-未提交的數(shù)據(jù)、未提交的事務(wù)-未開始的事務(wù)的分界標志位,以上就組成了當(dāng)前事務(wù)的一致性視圖(read-view)。
前面已經(jīng)提到了,所有的數(shù)據(jù)都是有版本的,這個版本即row trx_id。當(dāng)需要讀快照時,就需要讀取trx_id小于低水位且最大的trx_id版本號的數(shù)據(jù)。讀當(dāng)前就是讀取當(dāng)前最新版本的數(shù)據(jù),如果數(shù)據(jù)正在事務(wù)的處理中,那么久需要等待,這也是為什么會出現(xiàn)鎖等待超時的情況。
當(dāng)在RC的數(shù)據(jù)庫隔離級別下,每次的操作都會重新獲取當(dāng)前活躍的trx_id數(shù)組,形成新的一致性視圖,這就是sessionB 在t3 時刻讀取到了sessionC 在t2時刻提交的數(shù)據(jù),因為在新的一致性視圖中,sessionC 的trx_id 已經(jīng)已經(jīng)提交的事務(wù)了,所以就可以讀取到。在sessionA 的t5 時刻,拿到的一致性視圖中活躍的trx_id 數(shù)組中包含 sessionB 的活躍id,因為在更新數(shù)據(jù)時,需要當(dāng)前讀,而數(shù)據(jù)已經(jīng)被鎖住,所以出現(xiàn)了鎖超時的情況。
而在RR的數(shù)據(jù)庫隔離級別下,在事務(wù)開始時生成一個一致性視圖,此后在事務(wù)提交之前,其 trx_id 數(shù)組不會發(fā)生變化,這樣才能保證其可重復(fù)讀的特性。RR相對于RC實現(xiàn)簡單,區(qū)別在于是否在執(zhí)行語句前更新一致性視圖,活躍事務(wù)id的數(shù)組是否更新。
綜上,我們就知道了mvcc 是如何實現(xiàn)可重復(fù)讀和讀提交的隔離級別了。
4 ACID 的實現(xiàn)
- 事務(wù)的原子性 A 是通過 undolog 回滾日志來實現(xiàn)。
- 事務(wù)的持久性 C 性是通過 redolog 重做日志來實現(xiàn)的。
- 事務(wù)的隔離性 I 是通過 MVCC 來實現(xiàn)的。
- 原子性,持久性,隔離性都實現(xiàn)了,那么一致性 D 也就實現(xiàn)了。
redo log 是mysql innodb 引擎產(chǎn)生的物理日志,其大小有一定的限制,采用循環(huán)寫入的方式,寫滿之后進行刷盤。主要用于宕機后的數(shù)據(jù)恢復(fù)。redo log是一個循環(huán)寫入的日志,可以理解為一個環(huán),有 checkpoint 和 write pos 兩個標志點,checkpoint之前的空間是清除后的可寫空間,清除之前會更新到磁盤中,write pos是數(shù)據(jù)寫入的位置,當(dāng)兩個標志點相遇表明redo log已經(jīng)滿了,這時數(shù)據(jù)庫停止進行數(shù)據(jù)庫更新語句的執(zhí)行,轉(zhuǎn)而進行redo log日志同步到磁盤中。
binlog 是mysql server 層記錄邏輯的日志,可以一致追加寫入,主要用于主從數(shù)據(jù)同步。
到此這篇關(guān)于MYSQL數(shù)據(jù)庫Innodb 引擎mvcc鎖實現(xiàn)原理的文章就介紹到這了,更多相關(guān)mysql mvcc鎖實現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決Mysql數(shù)據(jù)庫插入數(shù)據(jù)出現(xiàn)問號(?)的解決辦法
這篇文章主要介紹了解決Mysql數(shù)據(jù)庫插入數(shù)據(jù)出現(xiàn)問號(?)的解決辦法的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-07-07
MySQL 存儲過程中執(zhí)行動態(tài)SQL語句的方法
這篇文章主要介紹了MySQL 存儲過程中執(zhí)行動態(tài)SQL語句的方法,需要的朋友可以參考下2014-08-08
MySQL數(shù)據(jù)庫管理常用命令小結(jié)
MySQL數(shù)據(jù)庫是一種開放源代碼的關(guān)系型數(shù)據(jù)庫管理系統(tǒng),MySQL數(shù)據(jù)庫系統(tǒng)使用最常用的數(shù)據(jù)庫管理語言--結(jié)構(gòu)化查詢語言(SQL)進行數(shù)據(jù)庫管理,MySQL數(shù)據(jù)庫管理有它自己獨特的使用命令,下面為您介紹MySQL數(shù)據(jù)庫管理常用命令。2011-03-03
SQL Server 完整備份遇到的一個不常見的錯誤及解決方法
這篇文章給大家介紹了SQL Server 完整備份遇到的一個不常見的錯誤及解決方法,非常不錯,具有一定的參考借鑒價值,需要的朋友參考下吧2019-05-05
mysql創(chuàng)建觸發(fā)器時報1064錯誤問題及解決
這篇文章主要介紹了mysql創(chuàng)建觸發(fā)器時報1064錯誤問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08
詳解如何通過Mysql的二進制日志恢復(fù)數(shù)據(jù)庫數(shù)據(jù)
本篇文章主要介紹了詳解如何通過Mysql的二進制日志恢復(fù)數(shù)據(jù)庫數(shù)據(jù),具有一定的參考價值,有興趣的可以了解一下。2017-04-04
MySQL創(chuàng)建帶特殊字符的數(shù)據(jù)庫名稱方法示例
這篇文章主要給大家介紹了MySQL創(chuàng)建帶特殊字符的數(shù)據(jù)庫名稱方法,文中給出了詳細的示例代碼,需要的朋友可以參考學(xué)習(xí),下面來一起看看吧。2017-03-03
update.where無索引導(dǎo)致MySQL死鎖問題解決
這篇文章主要為大家介紹了update.where無索引導(dǎo)致MySQL死鎖問題解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11

