前言
最近在學(xué)MySQL
肩钠,決定記錄一下碾褂,能寫多少寫多少兽间,不定時(shí)更新,加油正塌。
正文
分幾個(gè)部分來吧嘀略,大致如下:
字符集與比較規(guī)則
行格式與數(shù)據(jù)頁
InnoDB
索引訪問方法與連接
explain 與 子查詢優(yōu)化
redo
與undo
日志MVCC
與 鎖
本文為第四部分
MVCC
原理解析
主要就是想先寫這個(gè), 其他的后面有空再補(bǔ)~
由于我們跳過一些東西, 下面說MVCC的原理可能會(huì)各種懵
所以, 我們先簡單過幾個(gè)點(diǎn)吧.
一恤溶、溫故知新 -- 兩個(gè)隱藏列的含義
行格式的時(shí)候說過,InnoDB會(huì)為每行加上兩個(gè)隱藏列(row_id并不是必加的)trx_id
和 roll_pointer
這倆哥們簡直生猛的一塌糊涂.
小聲明:
事務(wù)T1的編號(hào)是100
事務(wù)T2的編號(hào)是200
-
trx_id
這個(gè)明顯是事務(wù)ID的意思, 就是記錄一下最近操作此條記錄的事務(wù)ID. 比如事務(wù)T1中插入了一條記錄X,那X的trx_id
就是100,如果在T1中繼續(xù)操作這條記錄是不會(huì)修改X的trx_id
的.如果事務(wù)T2修改了這X記錄,那么其trx_id
就變成200. -
roll_pointer
字面意思就是回滾指針, 指針存放的是一個(gè)地址, 指向了某個(gè)值. 它指向的是一條undo日志記錄
.
這里簡單提一下undo日志
,后面會(huì)專門寫一篇詳細(xì)講的[有生之年系列]帜羊。
一條undo日志
就是一條記錄, 存放在頁中,叫undo日志頁
.大致分為兩種,插入類型(insert
)和修改類型(update
咒程、delete
),這里只需要知道他們有一個(gè)很大區(qū)別:插入類型的undo日志是沒有指向下一條undo日志的屬性的,也就是說他們組成了一個(gè)undo日志鏈表讼育,又稱版本鏈
帐姻。
- 第一條是真實(shí)記錄,0是記錄類型奶段,H是記錄頭饥瓷,300是
trx_id
R是roll_pointer
, 1、3.. 是記錄的真實(shí)數(shù)據(jù) - 可以看到每條undo日志都有一個(gè)事務(wù)ID(圖中我畫在最后面,實(shí)際并不是存在最后),這個(gè)屬性其實(shí)是叫
old trx_id
, 表示當(dāng)前undo日志
對(duì)應(yīng)的事務(wù)ID痹籍,也稱此版本的創(chuàng)建事務(wù)ID
- 插入類型undo日志沒有
old roll_pointer
指向上一條roll_pointer
呢铆,因?yàn)樗緛砭褪?code>版本鏈的第一條 - 這條undo鏈表可以看出,此記錄由事務(wù)ID為100的事務(wù)插入蹲缠,在事務(wù)ID為200的事務(wù)中修改了兩次棺克,而事務(wù)ID為300的事務(wù)正在修改此記錄。
- 記住這個(gè)順序线定,后面有用娜谊。
二、老生常談 -- 隔離級(jí)別
說這個(gè)隔離級(jí)別之前斤讥,我們先想想為什么要有這個(gè)東東纱皆?
其實(shí)每個(gè)新技術(shù)或新名詞的出現(xiàn), 都可以問這幾個(gè)問題
1.這個(gè)東東解決了什么問題嗎?
2.現(xiàn)有的技術(shù)解決不了嗎?
3.如果能, 它比當(dāng)前的解決方案強(qiáng)在哪些方面呢?
4.不足或改進(jìn)之處.
- 以上純屬扯淡
隨著互聯(lián)網(wǎng)的發(fā)展,并發(fā)已經(jīng)是一道繞不開的坎周偎。各種問題都不斷冒出來,那并發(fā)事務(wù)訪問數(shù)據(jù)庫會(huì)發(fā)生什么樣的問題呢撑帖?
一個(gè)一個(gè)來看下蓉坎。
- 臟寫(
Dirty Write
)
T1事務(wù)開啟
T1修改:X=5
T2事務(wù)開始
T2修改:X=6
T2事務(wù)提交
T1事務(wù)提交
結(jié)果:X=5
這個(gè)時(shí)候T2的事務(wù)所做修改就丟失了.
一個(gè)事務(wù)修改了另一個(gè)未提交事務(wù)修改過的數(shù)據(jù),此為臟寫胡嘿。
- 臟讀(
Dirty Read
)
數(shù)據(jù)狀態(tài):X=5
T1事務(wù)開啟
T2事務(wù)開始
T2修改:X=6
T1讀闰劝:X=6
T2事務(wù)回滾
T1事務(wù)提交
T1讀到的X=6,庫中X=5
一個(gè)事務(wù)讀取了另一個(gè)未提交事務(wù)修改過的數(shù)據(jù)衷敌,此為臟讀勿侯。
- 不可重復(fù)讀(
Non-Repeatable Read
)
事務(wù)T1開啟
事務(wù)T2開啟
T1讀取:X=5
T2修改:X=6
事務(wù)T2提交
T1讀冉陕蕖:X=6
助琐。。面氓。
T1兩次讀取到的同一條記錄的值不一樣兵钮。
每次事務(wù)提交后蛆橡,當(dāng)前事務(wù)都能讀取到記錄的最新值,此為不可重復(fù)讀掘譬。
- 幻讀(
Phantom
)
庫中數(shù)據(jù): X=6
事務(wù)T1開啟
事務(wù)T2開啟
T1讀忍┭荨:X>5 (得到一條X=6)
T2新增:X=7
T1讀取:X>5 (得到兩條X=6和X=7)
葱轩。睦焕。。
T1第二次讀取的記錄數(shù)量比第一次讀取到的記錄數(shù)量多靴拱。
如果事務(wù)T1根據(jù)條件N查詢數(shù)據(jù)垃喊,事務(wù)T2添加了滿足條件N的記錄并提交了,T1再次根據(jù)N查詢數(shù)據(jù)能查詢T2新增的記錄缭嫡,此為幻讀缔御。
注意幾個(gè)點(diǎn):
- 不可重復(fù)讀是針對(duì)單條記錄的改動(dòng)(包括刪除與修改)
- 幻讀是針對(duì)查詢條件的范圍內(nèi)記錄的新增
- 幻讀只是針對(duì)新增,如果有范圍內(nèi)記錄的刪除或修改妇蛀,都屬于不可重復(fù)讀
為了解決這些問題耕突,有一幫人提出了一個(gè)SQL標(biāo)準(zhǔn),給出了四種隔離級(jí)別评架,用以解決上訴問題:
READ UNCOMMITTED
READ COMMITTED
READ REPEATABLE
SERIALABLE
SQL標(biāo)準(zhǔn)中規(guī)定眷茁,
-
READ UNCOMMITTED
解決臟寫 -
READ COMMITTED
解決臟讀 -
READ REPEATABLE
解決臟讀與不可重復(fù)讀 -
SERIALABLE
解決幻讀
數(shù)據(jù)庫對(duì)臟寫的問題是零容忍,哪怕最低的隔離級(jí)別都不允許出現(xiàn)
然而MySQL里的大佬還是牛逼纵诞,他們?cè)?code>READ REPEATABLE 級(jí)別就已經(jīng)解決了幻讀問題上祈。
實(shí)現(xiàn)一般有兩種方式,第一是加鎖浙芙,第二是MVCC
登刺。
實(shí)際上,二者都有用到.
三嗡呼、千呼萬喚 -- MVCC的原理
ReadView登場(chǎng)
先看下其大致結(jié)構(gòu)
-
m_ids
存放當(dāng)前系統(tǒng)中活躍的事務(wù)集合 -
min_trx_id
為m_ids
中最小的事務(wù)ID -
max_trx_id
分配給下一個(gè)開啟事務(wù)的事務(wù)ID -
creator_trx_id
創(chuàng)建此ReadView的事務(wù)ID
事務(wù)ID注意兩點(diǎn):
- 只有在對(duì)表中的記錄做改動(dòng)時(shí)(執(zhí)行
INSERT
纸俭、DELETE
、UPDATE
這些語句時(shí))才會(huì)為事務(wù)分配事務(wù)id南窗,否則在一個(gè)只讀事務(wù)中的事務(wù)id值都默認(rèn)為0揍很。- 按分配順序遞增
怎么突然蹦出來一個(gè)ReadView
? 先看看怎么用.
如果你不是一條魚的話,應(yīng)該還記得前面說過万伤,每條記錄都有一個(gè)版本鏈
吧窒悔,當(dāng)一個(gè)事務(wù)要訪問某條記錄時(shí),對(duì)著這個(gè)ReadView
的操作是這樣的:
- 判斷
trx_id
與creator_trx_id
是否相等敌买,是則意味著此版本正在被當(dāng)前事務(wù)操作简珠,可以訪問 - 判斷
trx_id
是否小于min_trx_id
,表示此版本的生成事務(wù)已經(jīng)提交虹钮,可以訪問 - 判斷
trx_id
是否大于等于max_trx_id
北救,表示此版本的生成事務(wù)已經(jīng)提交荐操,可以訪問 - 判斷
trx_id
是否在m_ids
中,若不在珍策,則意味著此版本的創(chuàng)建事務(wù)已經(jīng)提交托启,可以訪問;若在攘宙,則表明此版本在創(chuàng)建ReadView時(shí)還在活動(dòng)屯耸,不能訪問。
若無法訪問則順著版本鏈找下一個(gè)版本蹭劈,如果到最后一個(gè)版本(也就是Insert的undo日志)仍無法訪問疗绣,那么此記錄對(duì)當(dāng)前事務(wù)不可見。
現(xiàn)在知道這玩意有多牛逼了吧~
那跟隔離級(jí)別有啥關(guān)系呢铺韧?
READ COMMITTED
與 READ REPEATABLE
的最大區(qū)別就是生成ReadView
的時(shí)機(jī)不一樣
-
READ REPEATABLE
事務(wù)開啟時(shí)生成 -
READ COMMITTED
每次查詢前生成
想想
ReadView
與其生成時(shí)機(jī)如何能解決臟讀
/幻讀
問題~
喊我來加班多矮,到公司都寫完一篇MVCC了,還沒見到人~~~~[允悲]