更新時(shí)間:2021-01-20 17:08:00 來源:動(dòng)力節(jié)點(diǎn) 瀏覽1712次
MVCC(multiversion concurrency control),多版本并發(fā)控制,在MySQL數(shù)據(jù)庫中主要是通過在每一行記錄中增加三個(gè)字段,與undo log 中相關(guān)記錄配合使用,同時(shí)加上可見性算法,使得各個(gè)事務(wù)可以在不加鎖的情況下能夠同時(shí)地讀取到某行記錄上的準(zhǔn)確值(這個(gè)值對(duì)不同的事務(wù)而言可能是不同的)。使用MVCC,在不加鎖的情況下也能讀取到準(zhǔn)確的數(shù)據(jù),大大提高了并發(fā)效率。本文我們就來講MySQL中的MVCC。
那么為什么需要MVCC呢?數(shù)據(jù)庫通常使用鎖來實(shí)現(xiàn)隔離性。最原生的鎖,鎖住一個(gè)資源后會(huì)禁止其他任何線程訪問同一個(gè)資源。但是很多應(yīng)用的一個(gè)特點(diǎn)都是讀多寫少的場(chǎng)景,很多數(shù)據(jù)的讀取次數(shù)遠(yuǎn)大于修改的次數(shù),而讀取數(shù)據(jù)間互相排斥顯得不是很必要。所以就使用了一種讀寫鎖的方法,讀鎖和讀鎖之間不互斥,而寫鎖和寫鎖、讀鎖都互斥。這樣就很大提升了系統(tǒng)的并發(fā)能力。之后人們發(fā)現(xiàn)并發(fā)讀還是不夠,又提出了能不能讓讀寫之間也不沖突的方法,就是讀取數(shù)據(jù)時(shí)通過一種類似快照的方式將數(shù)據(jù)保存下來,這樣讀鎖就和寫鎖不沖突了,不同的事務(wù)session會(huì)看到自己特定版本的數(shù)據(jù)。當(dāng)然快照是一種概念模型,不同的數(shù)據(jù)庫可能用不同的方式來實(shí)現(xiàn)這種功能。
舉個(gè)例子,程序員A正在讀數(shù)據(jù)庫中某些內(nèi)容,而程序員B正在給這些內(nèi)容做修改(假設(shè)是在一個(gè)事務(wù)內(nèi)修改,大概持續(xù)10s左右),A在這10s內(nèi) 則可能看到一個(gè)不一致的數(shù)據(jù),在B沒有提交前,如何讓A能夠一直讀到的數(shù)據(jù)都是一致的呢?
有幾種處理方法,第一種: 基于鎖的并發(fā)控制,程序員B開始修改數(shù)據(jù)時(shí),給這些數(shù)據(jù)加上鎖,程序員A這時(shí)再讀,就發(fā)現(xiàn)讀取不了,處于等待情況,只能等B操作完才能讀數(shù)據(jù),這保證A不會(huì)讀到一個(gè)不一致的數(shù)據(jù),但是這個(gè)會(huì)影響程序的運(yùn)行效率。還有一種就是:MVCC,每個(gè)用戶連接數(shù)據(jù)庫時(shí),看到的都是某一特定時(shí)刻的數(shù)據(jù)庫快照,在B的事務(wù)沒有提交之前,A始終讀到的是某一特定時(shí)刻的數(shù)據(jù)庫快照,不會(huì)讀到B事務(wù)中的數(shù)據(jù)修改情況,直到B事務(wù)提交,才會(huì)讀取B的修改內(nèi)容。
一個(gè)支持MVCC的數(shù)據(jù)庫,在更新某些數(shù)據(jù)時(shí),并非使用新數(shù)據(jù)覆蓋舊數(shù)據(jù),而是標(biāo)記舊數(shù)據(jù)是過時(shí)的,同時(shí)在其他地方新增一個(gè)數(shù)據(jù)版本。因此,同一份數(shù)據(jù)有多個(gè)版本存儲(chǔ),但只有一個(gè)是最新的。
MVCC提供了時(shí)間一致性的處理思路,在MVCC下讀事務(wù)時(shí),通常使用一個(gè)時(shí)間戳或者事務(wù)ID來確定訪問哪個(gè)狀態(tài)的數(shù)據(jù)庫及哪些版本的數(shù)據(jù)。讀事務(wù)跟寫事務(wù)彼此是隔離開來的,彼此之間不會(huì)影響。假設(shè)同一份數(shù)據(jù),既有讀事務(wù)訪問,又有寫事務(wù)操作,實(shí)際上,寫事務(wù)會(huì)新建一個(gè)新的數(shù)據(jù)版本,而讀事務(wù)訪問的是舊的數(shù)據(jù)版本,直到寫事務(wù)提交,讀事務(wù)才會(huì)訪問到這個(gè)新的數(shù)據(jù)版本。
MVCC有兩種實(shí)現(xiàn)方式,第一種實(shí)現(xiàn)方式是將數(shù)據(jù)記錄的多個(gè)版本保存在數(shù)據(jù)庫中,當(dāng)這些不同版本數(shù)據(jù)不再需要時(shí),垃圾收集器回收這些記錄。這個(gè)方式被PostgreSQL和Firebird/Interbase采用,SQL Server使用的類似機(jī)制,所不同的是舊版本數(shù)據(jù)不是保存在數(shù)據(jù)庫中,而保存在不同于主數(shù)據(jù)庫的另外一個(gè)數(shù)據(jù)庫tempdb中。
第二種實(shí)現(xiàn)方式只在數(shù)據(jù)庫保存最新版本的數(shù)據(jù),但是會(huì)在使用undo時(shí)動(dòng)態(tài)重構(gòu)舊版本數(shù)據(jù),這種方式被Oracle和MySQL/InnoDB使用。
MVCC只在 READ COMMITTED 和 REPEATABLE READ 兩個(gè)隔離級(jí)別下工作。其他兩個(gè)隔離級(jí)別夠和MVCC不兼容, 因?yàn)?READ UNCOMMITTED 總是讀取最新的數(shù)據(jù)行, 而不是符合當(dāng)前事務(wù)版本的數(shù)據(jù)行。而 SERIALIZABLE 則會(huì)對(duì)所有讀取的行都加鎖。如果你是可重復(fù)讀隔離級(jí)別REPEATABLE_READ,這時(shí)候你的ReadView還是第一次select時(shí)候生成的ReadView,也就是列表的值還是[100]。所以select的結(jié)果是強(qiáng)哥1。所以第二次select結(jié)果和第一次一樣,所以叫可重復(fù)讀!也就是說已提交讀隔離級(jí)別下的事務(wù)在每次查詢的開始都會(huì)生成一個(gè)獨(dú)立的ReadView,而可重復(fù)讀隔離級(jí)別則在第一次讀的時(shí)候生成一個(gè)ReadView,之后的讀都復(fù)用之前的ReadView。
以上就是MySQL中的MVCC,通過版本鏈,實(shí)現(xiàn)多版本,可并發(fā)讀-寫,寫-讀。通過ReadView生成策略的不同實(shí)現(xiàn)不同的隔離級(jí)別。這也是MySQL數(shù)據(jù)庫中非常重要的幾個(gè)機(jī)制之一,想要了解更多MySQL數(shù)據(jù)庫中的機(jī)制,請(qǐng)觀看本站的MySQL教程,名師為你講解最新最全的MySQL數(shù)據(jù)庫知識(shí)!
Java實(shí)驗(yàn)班
0基礎(chǔ) 0學(xué)費(fèi) 15天面授
Java就業(yè)班
有基礎(chǔ) 直達(dá)就業(yè)
Java夜校直播班
業(yè)余時(shí)間 高薪轉(zhuǎn)行
Java在職加薪班
工作1~3年,加薪神器
Java架構(gòu)師班
工作3~5年,晉升架構(gòu)
提交申請(qǐng)后,顧問老師會(huì)電話與您溝通安排學(xué)習(xí)