一、并發(fā)控制基本知識
? 數(shù)據(jù)庫是共享資源划址,通常有許多個事務(wù)同時(shí)在運(yùn)行,當(dāng)多個事務(wù)并發(fā)地存取同一個數(shù)據(jù)庫時(shí)就會產(chǎn)生沖突,若對并發(fā)操作不加控制就可能會存取和存儲不正確的數(shù)據(jù)评汰,破壞數(shù)據(jù)庫的一致性。所以數(shù)據(jù)庫管理系統(tǒng)必須提供并發(fā)控制機(jī)制痢虹。
當(dāng)多個事務(wù)同時(shí)對數(shù)據(jù)庫進(jìn)行操作時(shí)被去,會出現(xiàn)3種沖突情形:
- 讀-讀:不存在任何問題。
- 讀-寫:有隔離性問題奖唯,可能遇到臟讀(會讀到未提交的數(shù)據(jù))惨缆,幻影讀等。
- 寫-寫:可能丟失更新數(shù)據(jù)丰捷。
1.1 讀寫鎖
? 為了最大化數(shù)據(jù)庫事務(wù)的并發(fā)能力坯墨,數(shù)據(jù)庫在處理并發(fā)讀或者寫的時(shí)候,可以通過一個由兩種類型的鎖組成的鎖系統(tǒng)來解決問題病往,這兩種類型的鎖分別是共享鎖和互斥鎖捣染,也叫做讀鎖和寫鎖。
? 讀鎖是共享的停巷,多個客戶在同一時(shí)刻讀取同一個資源的時(shí)候液斜,互不干擾。寫鎖是排他的叠穆,一個寫鎖會阻塞其他讀鎖以及其他寫鎖少漆,保證在給定的時(shí)間段中,只能有一個用戶能執(zhí)行寫入操作硼被。
1.2 鎖粒度與鎖策略
? 鎖粒度顧名思義就是加鎖時(shí)需要鎖住的范圍有多大(讓鎖對象更有選擇性)示损,這個概念的提出是為了提高共享資源的并發(fā)性,只對需要修改的資源進(jìn)行精確的鎖定嚷硫。
? 鎖策略就是在鎖的開銷和數(shù)據(jù)的安全性之間尋求平衡检访。因?yàn)樵诩渔i的時(shí)候也需要消耗資源,鎖的各種操作仔掸,包括獲得鎖脆贵、檢查鎖是否已經(jīng)解除、釋放鎖等起暮,都會增加系統(tǒng)的開銷卖氨。如果系統(tǒng)花費(fèi)大量的時(shí)間來管理鎖,而不是存取數(shù)據(jù),那么系統(tǒng)的性能可能會因此受到影響筒捺。
1.3 表鎖與行級鎖
? 表鎖與行級鎖是兩種比較重要鎖策略(MySQL中還有一種為頁級鎖)柏腻。
? 表鎖是MySQL中最基本的鎖策略,鎖定是MySQL各存儲引擎中最大顆粒度的鎖定機(jī)制(每次鎖定一張表)系吭。該鎖定機(jī)制最大的特點(diǎn)是實(shí)現(xiàn)邏輯非常簡單五嫂,帶來的系統(tǒng)負(fù)面影響最小。所以獲取鎖和釋放鎖的速度很快肯尺。由于表級鎖一次會將整個表鎖定沃缘,所以可以很好的避免死鎖問題。使用表級鎖定的主要是MyISAM则吟,MEMORY孩灯,CSV等一些非事務(wù)性存儲引擎。
? 行級鎖可以最大程度的支持并發(fā)處理逾滥,同時(shí)鎖定對象的顆粒度很小峰档,也是目前各大數(shù)據(jù)庫管理軟件所實(shí)現(xiàn)的鎖定顆粒度最小的。雖然能夠在并發(fā)處理能力上面有較大的優(yōu)勢寨昙,但是行級鎖定也因此帶來了不少弊端讥巡。由于鎖定資源的顆粒度很小,所以每次獲取鎖和釋放鎖需要的開銷更大了舔哪。此外欢顷,行級鎖定也最容易發(fā)生死鎖。使用行級鎖定的主要是InnoDB存儲引擎捉蚤。
二抬驴、MVCC
? MVCC,多版本的并發(fā)控制,英文全稱:Multi Version Concurrency Control缆巧。是數(shù)據(jù)庫中常用的解決讀-寫沖突的操作布持。前面提到,行級鎖是主要用于處理事務(wù)的鎖陕悬,但是在MySQL中大多事務(wù)型存儲引擎實(shí)現(xiàn)的都不是簡單的行級鎖题暖,而是多版本并發(fā)控制(MVCC),可以簡單的認(rèn)為MVCC是行級鎖的一個變種捉超。除了MySQL胧卤,其他數(shù)據(jù)庫也實(shí)現(xiàn)了MVCC,但是各自的實(shí)現(xiàn)機(jī)制不同拼岳,沒有一個統(tǒng)一的標(biāo)準(zhǔn)枝誊。
? 在MVCC中,很多情況下都避免了加鎖操作惜纸,因此開銷更低叶撒,大多數(shù)標(biāo)準(zhǔn)的MVCC都實(shí)現(xiàn)令非阻塞的讀操作绝骚,寫操作也只鎖定必要的行。
? MVCC的實(shí)現(xiàn)痊乾,是通過保存數(shù)據(jù)在某個時(shí)間點(diǎn)的快照實(shí)現(xiàn)的。就是當(dāng)我們在修改數(shù)據(jù)的時(shí)候椭更,可以為這條數(shù)據(jù)創(chuàng)建一個快照哪审,后面就可以直接讀取這個快照。
2.1InnoDB MVCC實(shí)現(xiàn)原理
? 在InnoDB的MVCC虑瀑,是通過每行記錄后面保存的兩個隱藏列實(shí)現(xiàn)的湿滓。
? 每一行記錄都有兩個隱藏列: DATA_TRX_ID(保存了行的建立時(shí)間)、 DATA_ROLL_PTR(保持了行的過期時(shí)間或刪除時(shí)間)舌狗。保存的時(shí)間值指的是系統(tǒng)版本號而不是真正的時(shí)間叽奥,同時(shí)每開始一個新的事務(wù)時(shí),系統(tǒng)的版本號就會遞增痛侍。
? 在進(jìn)行事務(wù)的操作前朝氓,MVCC設(shè)置了以下規(guī)則:
SELECT
? InnoDB會根據(jù)以下兩個條件檢查每行紀(jì)錄:
InnoDB只查找版本早于當(dāng)前事務(wù)版本的數(shù)據(jù)行,即主届,行的系統(tǒng)版本號小于或等于事務(wù)的系統(tǒng)版本號赵哲,這樣可以確保事務(wù)讀取的行,要么是在事務(wù)開始前已經(jīng)存在的君丁,要么是事務(wù)自身插入或者修改過的枫夺。
-
行的刪除版本,要么未定義绘闷,要么大于當(dāng)前事務(wù)版本號橡庞。這樣可以確保事務(wù)讀取到的行,在事務(wù)開始之前未被刪除印蔗。
只有符合上述兩個條件的紀(jì)錄扒最,才能作為查詢結(jié)果返回。
INSERT
? InnoDB為插入的每一行保存當(dāng)前系統(tǒng)版本號作為行版本號华嘹。
DELETE
? InnoDB為刪除的每一行保存當(dāng)前系統(tǒng)版本號作為行刪除標(biāo)識扼倘。
UPDATE
? InnoDB為插入一行新紀(jì)錄,保存當(dāng)前系統(tǒng)版本號作為行版本號除呵,同時(shí)再菊,保存當(dāng)前系統(tǒng)版本號到原來的行作為行刪除標(biāo)識。
2.2 InnoDB MVCC實(shí)現(xiàn)實(shí)例
? 首先需要了解的一個概念是ReadView颜曾,ReadView中主要就是有個列表來存儲我們系統(tǒng)中當(dāng)前活躍著的讀寫事務(wù)纠拔,也就是開始了還未提交的事務(wù)。通過這個列表來判斷記錄的某個版本是否對當(dāng)前事務(wù)可見泛豪。
? 以可重復(fù)讀隔離級別為例稠诲,假設(shè)當(dāng)前列表里的事務(wù)id為[80,100]侦鹏。在可重復(fù)讀隔離級別,這時(shí)候我的ReadView還是第一次select時(shí)候生成的ReadView,也就是列表的值還是[100]臀叙。當(dāng)我需要執(zhí)行一次select語句時(shí)
? 1.如果我需要訪問的記錄版本的事務(wù)id為50略水,比當(dāng)前列表最小的id80小,那說明這個事務(wù)在之前就提交了劝萤,所以對當(dāng)前活動的事務(wù)來說是可訪問的渊涝。
? 2.如果我需要訪問的記錄版本的事務(wù)id為70,發(fā)現(xiàn)此事務(wù)在列表id最大值和最小值之間,那就再判斷一下是否在列表內(nèi)床嫌,如果在那就說明此事務(wù)還未提交跨释,所以版本不能被訪問。如果不在那說明事務(wù)已經(jīng)提交厌处,所以版本可以被訪問鳖谈。
? 3.如果我要訪問的記錄版本的事務(wù)id為110,那比事務(wù)列表最大id100都大阔涉,那說明這個版本是在ReadView生成之后才發(fā)生的缆娃,所以不能被訪問。
2.3 MVCC特點(diǎn)
? 在MVCC中瑰排,不管執(zhí)行多少時(shí)間龄恋,每個事務(wù)看到的數(shù)據(jù)都是一致的;而根據(jù)事務(wù)開始的時(shí)間不同凶伙,每個事務(wù)對同一張表郭毕,看到的同一時(shí)間看到的數(shù)據(jù)也不同。
? MVCC在大多數(shù)情況下代替了行鎖函荣,實(shí)現(xiàn)了對讀的非阻塞显押,讀不加鎖,讀寫不沖突傻挂。缺點(diǎn)是每行記錄都需要額外的存儲空間乘碑,需要做更多的行維護(hù)和檢查工作。MVCC手段只適用于Msyql隔離級別中的讀已提交(Read committed)和可重復(fù)讀(Repeatable Read)金拒,因?yàn)樽x未提交會讀取最新的數(shù)據(jù)行兽肤,而可串行化則會將讀取的行都加鎖。
三绪抛、補(bǔ)充
? 在并發(fā)控制中资铡,要解決沖突,總共由三種方式:
? 1.(悲觀)鎖幢码,即基于鎖的并發(fā)控制笤休,比如2PL,這種方式開銷比較高症副,而且無法避免死鎖店雅。
? 2.多版本并發(fā)控制(MVCC)政基,是一種用來解決讀-寫沖突的無鎖并發(fā)控制。
? 3.樂觀并發(fā)控制(OCC)是一種用來解決寫-寫沖突的無鎖并發(fā)控制闹啦,認(rèn)為事務(wù)間爭用沒有那么多沮明,所以先進(jìn)行修改,在提交事務(wù)前窍奋,檢查一下事務(wù)開始后荐健,有沒有新提交改變,如果沒有就提交费变,如果有就放棄并重試摧扇。樂觀并發(fā)控制類似自選鎖圣贸。樂觀并發(fā)控制適用于低數(shù)據(jù)爭用挚歧,寫沖突比較少的環(huán)境。
? 在使用MVCC控制的時(shí)候吁峻,可以以結(jié)合基于鎖的并發(fā)控制來解決寫-寫沖突滑负,即MVCC+2PL;也可以結(jié)合樂觀并發(fā)控制來解決寫-寫沖突用含。