上一篇文章,介紹了隔離級別,MySQL默認是使用可重復(fù)讀,但是在可重復(fù)讀的級別下,可能會出現(xiàn)幻讀,也就是讀取到另一個session添加的數(shù)據(jù),那么除了配合使用間隙鎖的方式,還使用了MVCC機制解決,保證在可重復(fù)讀的場景下,同一個session讀取的數(shù)據(jù)一致性。
mvcc機制
MVCC(Multi-Version Concurrency Control) 多版本并發(fā)控制機制,對同一行數(shù)據(jù)的讀和寫操作默認不會加鎖互斥保證隔離型,提高性能,而串行化隔離級別為了保證較高的隔離型是將所有操作通過互斥來實現(xiàn)的。
Mysql在讀已提交和可重復(fù)讀隔離級別下都實現(xiàn)了MVCC機制。
原理
其實undo日志鏈是指一行數(shù)據(jù)被多個事務(wù)依次修改過后,每個事務(wù)修改完后,mysql都會保留修改前的數(shù)據(jù)undo 回滾日志,并且添加兩個隱藏字段trx_id
和 roll_pointer
將undo日志鏈串聯(lián)形成一個歷史記錄版本鏈。 通過數(shù)據(jù)快照的方式。關(guān)鍵核心是undo日志和readView
什么時候會生產(chǎn)trx-id ?
在begin transaction的時候并不會新建,在執(zhí)行到他們之后的第一個修改操作InnoDB表的語句的時候,事務(wù)才真正啟動,向mysql申請事務(wù)id,mysql內(nèi)部是嚴格按照事務(wù)的啟動順序來分配事務(wù)id的。
一個案例
mysql> CREATE TABLE `t` (
`id` int(11) NOT NULL,
`k` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1),(2,2);
最終結(jié)果事務(wù)A讀取的是1,而事務(wù)B讀取的是3。為什么是這樣,我們來分析一下。
假設(shè)在事務(wù)A開始的時候只有一個transaciton id = 1, 那么事務(wù)A就是2,事務(wù)B就是3,事務(wù)C就是4.
事務(wù)A的試圖數(shù)組就是[1,2] 事務(wù)B視圖數(shù)組[1,2,3] , 事務(wù)C視圖數(shù)組[1,2,3,4];
當事務(wù)C進行修改k=k+1 ,就將id=1的k 設(shè)置為2。但是接著事務(wù)B也加1操作,此時事務(wù)B的+1操作其實是當前讀,也就是獲取最新的數(shù)據(jù),k=2, 在k=2的基礎(chǔ)上+1 操作,那么k=3。所以事務(wù)B的K值事3。但是事務(wù)A的視圖數(shù)組[1,2] 查詢undo日志鏈,發(fā)現(xiàn) 【3,4】都查看不了,所以k=1;
對比規(guī)則
-
如果 row 的 trx_id 落在綠色部分( trx_id<min_id ),表示這個版本是已提交的事務(wù)生成的,這個數(shù)據(jù)是可見的;
-
如果 row 的 trx_id 落在紅色部分( trx_id>max_id ),表示這個版本是由將來啟動的事務(wù)生成的,是不可見的(若 row 的 trx_id 就是當前自己的事務(wù)是可見的);
-
如果 row 的 trx_id 落在黃色部分(min_id <=trx_id<= max_id),那就包括兩種情況
a. 若 row 的 trx_id 在視圖數(shù)組中,表示這個版本是由還沒提交的事務(wù)生成的,不可見(若 row 的 trx_id 就是當前自己的事務(wù)是可見的);
b. 若 row 的 trx_id 不在視圖數(shù)組中,表示這個版本是已經(jīng)提交了的事務(wù)生成的,可見
一個簡易的版本就是
- 版本未提交,不可見;
- 版本已提交,但是是在視圖創(chuàng)建后提交的,不可見;
- 版本已提交,而且是在視圖創(chuàng)建前提交的,可見。
MVCC機制的實現(xiàn)就是通過read-view機制與undo版本鏈比對機制,使得不同的事務(wù)會根據(jù)數(shù)據(jù)版本鏈對比規(guī)則讀取 同一條數(shù)據(jù)在版本鏈上的不同版本數(shù)據(jù)。
不同的讀操作
select * from table where ?;
select * from table where ? lock in share mode; # 加讀鎖 select * from table where ? for update;# 加寫鎖
insert into table values (...);# 加寫鎖
update table set ? where ?;# 加寫鎖
delete from table where ?;# 加寫鎖
# 所有以上的語句,都屬于當前讀,讀取記錄的最新版本。并且,讀取之后,還需要保證其他并發(fā) 事務(wù)不能修改當前記錄,對讀取記錄加鎖。
# 其中,除了第一條語句,對讀取記錄加讀鎖外,其他的操作都加的是寫鎖。
那么思考一個邏輯。如果一個事務(wù)A 對id=1更新操作的時候,還沒有提交,那么事務(wù)B也對id=2更新操作,會出現(xiàn)什么情況?
答案就是會阻塞事務(wù)B,必須等事務(wù)A執(zhí)行完畢。
bufferpool緩存
在我們更新一條SQL數(shù)據(jù)的時候,大概流程如下
1.構(gòu)建連接、查詢緩存、分析器、優(yōu)化器、執(zhí)行器
2.在執(zhí)行器的時候
- 如果buffer pool有對應(yīng)的頁數(shù)據(jù),直接獲取,否則從磁盤加載對應(yīng)的id=1的數(shù)據(jù) name=zhuge。
- 將name=‘zhuge’ 進行寫入undo日志文件中,(主要方式如果事務(wù)進行回滾的話,可以直接恢復(fù)數(shù)據(jù))
- 更新內(nèi)存中的buffer pool的數(shù)據(jù) name=‘zhuge 666’
- 寫入redo log日志。準備階段。 (系統(tǒng)宕機,用于恢復(fù)數(shù)據(jù) 重做)
- 寫入bin log日志,然后提交事務(wù)。
我們來思考下,為什么需要設(shè)計一套這么復(fù)雜的,因為主要是對于磁盤的操作是隨機IO性能不高,可以通過寫入LOG文件,提升性能。先更新到BufferPool中,然后順序?qū)懭罩疚募?。也可以保證各種異常情況下數(shù)據(jù)的一致性。
幾個小問題?
1.臟頁刷盤的時機?(大概四種 a.redolog滿了 binnodl buffer滿了 c:myg!正常關(guān)閉 d.mysql空閑)
2.如果數(shù)據(jù)庫突然奔潰了,沒刷盤的數(shù)據(jù)是不是就丟了?(不會,redolog防崩潰)
3.如果redo.log沒寫入磁盤,這時候這部分事務(wù)是不是數(shù)據(jù)就丟了(redolog buffer 里的數(shù)據(jù)丟了怎么辦,redolog buffer記錄的是 事務(wù)prepare階段數(shù)據(jù)(未提交 丟了無所謂))
4.如果redolog在刷盤的時候斷電呢。文章來源:http://www.zghlxwxcb.cn/news/detail-808103.html
總結(jié)
MySQL的事務(wù)是如何保證的,我們用了兩篇文章進行詳細描述,通過ACID,其中AID是為了保證C。
(隔離性):MVCC原理、(原子性):innodb 事務(wù)二階段提交、D(持久性):事務(wù)提交后的數(shù)據(jù)落盤。以及通過相關(guān)的鎖機制,來保證。文章來源地址http://www.zghlxwxcb.cn/news/detail-808103.html
到了這里,關(guān)于【數(shù)據(jù)庫】聊聊MVCC機制與BufferPool緩存機制的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!