Innodb中的事務(wù)隔離級別和鎖的關(guān)系

我們都知道事務(wù)的幾種性質(zhì)钝计,數(shù)據(jù)庫為了維護(hù)這些性質(zhì),尤其是一致性和隔離性齐佳,一般使用加鎖這種方式私恬。同時數(shù)據(jù)庫又是個高并發(fā)的應(yīng)用,同一時間會有大量的并發(fā)訪問炼吴,如果加鎖過度本鸣,會極大的降低并發(fā)處理能力。所以對于加鎖的處理硅蹦,可以說就是數(shù)據(jù)庫對于事務(wù)處理的精髓所在荣德。這里通過分析MySQL中InnoDB引擎的加鎖機(jī)制,來拋磚引玉童芹,讓讀者更好的理解涮瞻,在事務(wù)處理中數(shù)據(jù)庫到底做了什么。

#一次封鎖or兩段鎖假褪?

因為有大量的并發(fā)訪問署咽,為了預(yù)防死鎖,一般應(yīng)用中推薦使用一次封鎖法生音,就是在方法的開始階段宁否,已經(jīng)預(yù)先知道會用到哪些數(shù)據(jù),然后全部鎖住久锥,在方法運(yùn)行之后家淤,再全部解鎖异剥。這種方式可以有效的避免循環(huán)死鎖瑟由,但在數(shù)據(jù)庫中卻不適用,因為在事務(wù)開始階段,數(shù)據(jù)庫并不知道會用到哪些數(shù)據(jù)歹苦。

數(shù)據(jù)庫遵循的是兩段鎖協(xié)議青伤,將事務(wù)分成兩個階段,加鎖階段和解鎖階段(所以叫兩段鎖)

加鎖階段:在該階段可以進(jìn)行加鎖操作殴瘦。在對任何數(shù)據(jù)進(jìn)行讀操作之前要申請并獲得S鎖(共享鎖狠角,其它事務(wù)可以繼續(xù)加共享鎖,但不能加排它鎖)蚪腋,在進(jìn)行寫操作之前要申請并獲得X鎖(排它鎖丰歌,其它事務(wù)不能再獲得任何鎖)。加鎖不成功屉凯,則事務(wù)進(jìn)入等待狀態(tài)立帖,直到加鎖成功才繼續(xù)執(zhí)行。

解鎖階段:當(dāng)事務(wù)釋放了一個封鎖以后悠砚,事務(wù)進(jìn)入解鎖階段晓勇,在該階段只能進(jìn)行解鎖操作不能再進(jìn)行加鎖操作。

事務(wù)加鎖/解鎖處理

begin灌旧;

insert into test .....加insert對應(yīng)的鎖

update test set...加update對應(yīng)的鎖

delete from test ....加delete對應(yīng)的鎖

commit;事務(wù)提交時绑咱,同時釋放insert、update枢泰、delete對應(yīng)的鎖

這種方式雖然無法避免死鎖描融,但是兩段鎖協(xié)議可以保證事務(wù)的并發(fā)調(diào)度是串行化(串行化很重要,尤其是在數(shù)據(jù)恢復(fù)和備份的時候)的稼稿。

#事務(wù)中的加鎖方式

##事務(wù)的四種隔離級別

在數(shù)據(jù)庫操作中,為了有效保證并發(fā)讀取數(shù)據(jù)的正確性讳窟,提出的事務(wù)隔離級別让歼。我們的數(shù)據(jù)庫鎖,也是為了構(gòu)建這些隔離級別存在的丽啡。

隔離級別臟讀(Dirty Read)不可重復(fù)讀(NonRepeatable Read)幻讀(Phantom Read)

未提交讀(Read uncommitted)可能可能可能

已提交讀(Read committed)不可能可能可能

可重復(fù)讀(Repeatable read)不可能不可能可能

可串行化(Serializable )不可能不可能不可能

未提交讀(Read Uncommitted):允許臟讀谋右,也就是可能讀取到其他會話中未提交事務(wù)修改的數(shù)據(jù)

提交讀(Read Committed):只能讀取到已經(jīng)提交的數(shù)據(jù)。Oracle等多數(shù)數(shù)據(jù)庫默認(rèn)都是該級別 (不重復(fù)讀)

可重復(fù)讀(Repeated Read):可重復(fù)讀补箍。在同一個事務(wù)內(nèi)的查詢都是事務(wù)開始時刻一致的改执,InnoDB默認(rèn)級別。在SQL標(biāo)準(zhǔn)中坑雅,該隔離級別消除了不可重復(fù)讀辈挂,但是還存在幻象讀

串行讀(Serializable):完全串行化的讀,每次讀都需要獲得表級共享鎖裹粤,讀寫相互都會阻塞

Read Uncommitted這種級別终蒂,數(shù)據(jù)庫一般都不會用,而且任何操作都不會加鎖,這里就不討論了拇泣。

##MySQL中鎖的種類

MySQL中鎖的種類很多噪叙,有常見的表鎖和行鎖,也有新加入的Metadata Lock等等,表鎖是對一整張表加鎖霉翔,雖然可分為讀鎖和寫鎖睁蕾,但畢竟是鎖住整張表,會導(dǎo)致并發(fā)能力下降债朵,一般是做ddl處理時使用子眶。

行鎖則是鎖住數(shù)據(jù)行,這種加鎖方法比較復(fù)雜序芦,但是由于只鎖住有限的數(shù)據(jù)壹店,對于其它數(shù)據(jù)不加限制,所以并發(fā)能力強(qiáng)芝加,MySQL一般都是用行鎖來處理并發(fā)事務(wù)硅卢。這里主要討論的也就是行鎖。

###Read Committed(讀取提交內(nèi)容)

在RC級別中藏杖,數(shù)據(jù)的讀取都是不加鎖的将塑,但是數(shù)據(jù)的寫入、修改和刪除是需要加鎖的蝌麸。效果如下

MySQL> show create table class_teacher \G\

Table: class_teacher

Create Table: CREATE TABLE `class_teacher` (

? `id` int(11) NOT NULL AUTO_INCREMENT,

? `class_name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,

? `teacher_id` int(11) NOT NULL,

? PRIMARY KEY (`id`),

? KEY `idx_teacher_id` (`teacher_id`)

) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

1 row in set (0.02 sec)

MySQL> select * from class_teacher;

+----+--------------+------------+

| id | class_name? | teacher_id |

+----+--------------+------------+

|? 1 | 初三一班? ? |? ? ? ? ? 1 |

|? 3 | 初二一班? ? |? ? ? ? ? 2 |

|? 4 | 初二二班? ? |? ? ? ? ? 2 |

+----+--------------+------------+

由于MySQL的InnoDB默認(rèn)是使用的RR級別点寥,所以我們先要將該session開啟成RC級別,并且設(shè)置binlog的模式

SETsessiontransactionisolationlevelreadcommitted;SETSESSIONbinlog_format ='ROW';(或者是MIXED)

事務(wù)A事務(wù)B

begin;begin;

update class_teacher set class_name='初三二班' where teacher_id=1;update class_teacher set class_name='初三三班' where teacher_id=1;

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

commit;

為了防止并發(fā)過程中的修改沖突来吩,事務(wù)A中MySQL給teacher_id=1的數(shù)據(jù)行加鎖敢辩,并一直不commit(釋放鎖),那么事務(wù)B也就一直拿不到該行鎖弟疆,wait直到超時戚长。

這時我們要注意到,teacher_id是有索引的怠苔,如果是沒有索引的class_name呢同廉?update class_teacher set teacher_id=3 where class_name = '初三一班';

那么MySQL會給整張表的所有數(shù)據(jù)行的加行鎖。這里聽起來有點不可思議柑司,但是當(dāng)sql運(yùn)行的過程中迫肖,MySQL并不知道哪些數(shù)據(jù)行是 class_name = '初三一班'的(沒有索引嘛),如果一個條件無法通過索引快速過濾攒驰,存儲引擎層面就會將所有記錄加鎖后返回蟆湖,再由MySQL Server層進(jìn)行過濾。

但在實際使用過程當(dāng)中玻粪,MySQL做了一些改進(jìn)隅津,在MySQL Server過濾條件诬垂,發(fā)現(xiàn)不滿足后,會調(diào)用unlock_row方法饥瓷,把不滿足條件的記錄釋放鎖 (違背了二段鎖協(xié)議的約束)剥纷。這樣做痹籍,保證了最后只會持有滿足條件記錄上的鎖呢铆,但是每條記錄的加鎖操作還是不能省略的《撞可見即使是MySQL棺克,為了效率也是會違反規(guī)范的。(參見《高性能MySQL》中文第三版p181)

這種情況同樣適用于MySQL的默認(rèn)隔離級別RR线定。所以對一個數(shù)據(jù)量很大的表做批量修改的時候娜谊,如果無法使用相應(yīng)的索引,MySQL Server過濾數(shù)據(jù)的的時候特別慢斤讥,就會出現(xiàn)雖然沒有修改某些行的數(shù)據(jù)纱皆,但是它們還是被鎖住了的現(xiàn)象。

###Repeatable Read(可重讀)

這是MySQL中InnoDB默認(rèn)的隔離級別芭商。我們姑且分“讀”和“寫”兩個模塊來講解派草。

####讀

讀就是可重讀,可重讀這個概念是一事務(wù)的多個實例在并發(fā)讀取數(shù)據(jù)時铛楣,會看到同樣的數(shù)據(jù)行近迁,有點抽象,我們來看一下效果簸州。

RC(不可重讀)模式下的展現(xiàn)

事務(wù)A事務(wù)B

begin;begin;

select id,class_name,teacher_id from class_teacher where teacher_id=1;

idclass_nameteacher_id

1初三二班1

2初三一班1

update class_teacher set class_name='初三三班' where id=1;

commit;

select id,class_name,teacher_id from class_teacher where teacher_id=1;

idclass_nameteacher_id

1初三三班1

2初三一班1

讀到了事務(wù)B修改的數(shù)據(jù)鉴竭,和第一次查詢的結(jié)果不一樣,是不可重讀的岸浑。

commit;

事務(wù)B修改id=1的數(shù)據(jù)提交之后搏存,事務(wù)A同樣的查詢,后一次和前一次的結(jié)果不一樣矢洲,這就是不可重讀(重新讀取產(chǎn)生的結(jié)果不一樣)祭埂。這就很可能帶來一些問題,那么我們來看看在RR級別中MySQL的表現(xiàn):

事務(wù)A事務(wù)B事務(wù)C

begin;begin;begin;

select id,class_name,teacher_id from class_teacher where?teacher_id=1;

idclass_nameteacher_id

1初三二班1

2初三一班1

update class_teacher set class_name='初三三班' where id=1;

commit;

insert into class_teacher values (null,'初三三班',1);

commit;

select id,class_name,teacher_id from class_teacher where?teacher_id=1;

idclass_nameteacher_id

1初三二班1

2初三一班1

沒有讀到事務(wù)B修改的數(shù)據(jù)兵钮,和第一次sql讀取的一樣蛆橡,是可重復(fù)讀的。

沒有讀到事務(wù)C新添加的數(shù)據(jù)掘譬。

commit;

我們注意到泰演,當(dāng)teacher_id=1時,事務(wù)A先做了一次讀取葱轩,事務(wù)B中間修改了id=1的數(shù)據(jù)睦焕,并commit之后藐握,事務(wù)A第二次讀到的數(shù)據(jù)和第一次完全相同。所以說它是可重讀的垃喊。那么MySQL是怎么做到的呢猾普?這里姑且賣個關(guān)子,我們往下看本谜。

####不可重復(fù)讀和幻讀的區(qū)別####

很多人容易搞混不可重復(fù)讀和幻讀初家,確實這兩者有些相似褐隆。但不可重復(fù)讀重點在于update和delete椭更,而幻讀的重點在于insert烹看。

如果使用鎖機(jī)制來實現(xiàn)這兩種隔離級別批幌,在可重復(fù)讀中葬毫,該sql第一次讀取到數(shù)據(jù)后知残,就將這些數(shù)據(jù)加鎖吐咳,其它事務(wù)無法修改這些數(shù)據(jù)蹂楣,就可以實現(xiàn)可重復(fù)讀了赏参。但這種方法卻無法鎖住insert的數(shù)據(jù)志笼,所以當(dāng)事務(wù)A先前讀取了數(shù)據(jù),或者修改了全部數(shù)據(jù)把篓,事務(wù)B還是可以insert數(shù)據(jù)提交纫溃,這時事務(wù)A就會發(fā)現(xiàn)莫名其妙多了一條之前沒有的數(shù)據(jù),這就是幻讀纸俭,不能通過行鎖來避免皇耗。需要Serializable隔離級別 ,讀用讀鎖揍很,寫用寫鎖郎楼,讀鎖和寫鎖互斥,這么做可以有效的避免幻讀窒悔、不可重復(fù)讀呜袁、臟讀等問題,但會極大的降低數(shù)據(jù)庫的并發(fā)能力简珠。

所以說不可重復(fù)讀和幻讀最大的區(qū)別阶界,就在于如何通過鎖機(jī)制來解決他們產(chǎn)生的問題。

上文說的聋庵,是使用悲觀鎖機(jī)制來處理這兩種問題膘融,但是MySQL、ORACLE祭玉、PostgreSQL等成熟的數(shù)據(jù)庫氧映,出于性能考慮,都是使用了以樂觀鎖為理論基礎(chǔ)的MVCC(多版本并發(fā)控制)來避免這兩種問題脱货。

####悲觀鎖和樂觀鎖####

悲觀鎖

正如其名岛都,它指的是對數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù)律姨,以及來自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度,因此臼疫,在整個數(shù)據(jù)處理過程中择份,將數(shù)據(jù)處于鎖定狀態(tài)。悲觀鎖的實現(xiàn)烫堤,往往依靠數(shù)據(jù)庫提供的鎖機(jī)制(也只有數(shù)據(jù)庫層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問的排他性荣赶,否則,即使在本系統(tǒng)中實現(xiàn)了加鎖機(jī)制塔逃,也無法保證外部系統(tǒng)不會修改數(shù)據(jù))讯壶。

在悲觀鎖的情況下料仗,為了保證事務(wù)的隔離性湾盗,就需要一致性鎖定讀。讀取數(shù)據(jù)時給加鎖立轧,其它事務(wù)無法修改這些數(shù)據(jù)格粪。修改刪除數(shù)據(jù)時也要加鎖,其它事務(wù)無法讀取這些數(shù)據(jù)氛改。

樂觀鎖

相對悲觀鎖而言帐萎,樂觀鎖機(jī)制采取了更加寬松的加鎖機(jī)制。悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫的鎖機(jī)制實現(xiàn)胜卤,以保證操作最大程度的獨占性疆导。但隨之而來的就是數(shù)據(jù)庫性能的大量開銷,特別是對長事務(wù)而言葛躏,這樣的開銷往往無法承受澈段。

而樂觀鎖機(jī)制在一定程度上解決了這個問題。樂觀鎖舰攒,大多是基于數(shù)據(jù)版本( Version )記錄機(jī)制實現(xiàn)败富。何謂數(shù)據(jù)版本?即為數(shù)據(jù)增加一個版本標(biāo)識摩窃,在基于數(shù)據(jù)庫表的版本解決方案中兽叮,一般是通過為數(shù)據(jù)庫表增加一個 “version” 字段來實現(xiàn)。讀取出數(shù)據(jù)時猾愿,將此版本號一同讀出鹦聪,之后更新時,對此版本號加一蒂秘。此時泽本,將提交數(shù)據(jù)的版本數(shù)據(jù)與數(shù)據(jù)庫表對應(yīng)記錄的當(dāng)前版本信息進(jìn)行比對,如果提交的數(shù)據(jù)版本號大于數(shù)據(jù)庫表當(dāng)前版本號材彪,則予以更新观挎,否則認(rèn)為是過期數(shù)據(jù)琴儿。

要說明的是,MVCC的實現(xiàn)沒有固定的規(guī)范嘁捷,每個數(shù)據(jù)庫都會有不同的實現(xiàn)方式造成,這里討論的是InnoDB的MVCC。

####MVCC在MySQL的InnoDB中的實現(xiàn)

在InnoDB中雄嚣,會在每行數(shù)據(jù)后添加兩個額外的隱藏的值來實現(xiàn)MVCC晒屎,這兩個值一個記錄這行數(shù)據(jù)何時被創(chuàng)建,另外一個記錄這行數(shù)據(jù)何時過期(或者被刪除)缓升。 在實際操作中鼓鲁,存儲的并不是時間,而是事務(wù)的版本號港谊,每開啟一個新事務(wù)骇吭,事務(wù)的版本號就會遞增。 在可重讀Repeatable reads事務(wù)隔離級別下:

SELECT時歧寺,讀取創(chuàng)建版本號<=當(dāng)前事務(wù)版本號燥狰,刪除版本號為空或>當(dāng)前事務(wù)版本號。

INSERT時斜筐,保存當(dāng)前事務(wù)版本號為行的創(chuàng)建版本號

DELETE時龙致,保存當(dāng)前事務(wù)版本號為行的刪除版本號

UPDATE時,插入一條新紀(jì)錄顷链,保存當(dāng)前事務(wù)版本號為行創(chuàng)建版本號目代,同時保存當(dāng)前事務(wù)版本號到原來刪除的行

通過MVCC,雖然每行記錄都需要額外的存儲空間嗤练,更多的行檢查工作以及一些額外的維護(hù)工作榛了,但可以減少鎖的使用,大多數(shù)讀操作都不用加鎖潭苞,讀數(shù)據(jù)操作很簡單忽冻,性能很好,并且也能保證只會讀取到符合標(biāo)準(zhǔn)的行此疹,也只鎖住必要行僧诚。

我們不管從數(shù)據(jù)庫方面的教課書中學(xué)到,還是從網(wǎng)絡(luò)上看到蝗碎,大都是上文中事務(wù)的四種隔離級別這一模塊列出的意思湖笨,RR級別是可重復(fù)讀的,但無法解決幻讀蹦骑,而只有在Serializable級別才能解決幻讀慈省。于是我就加了一個事務(wù)C來展示效果。在事務(wù)C中添加了一條teacher_id=1的數(shù)據(jù)commit眠菇,RR級別中應(yīng)該會有幻讀現(xiàn)象边败,事務(wù)A在查詢teacher_id=1的數(shù)據(jù)時會讀到事務(wù)C新加的數(shù)據(jù)袱衷。但是測試后發(fā)現(xiàn),在MySQL中是不存在這種情況的笑窜,在事務(wù)C提交后致燥,事務(wù)A還是不會讀到這條數(shù)據(jù)∨沤兀可見在MySQL的RR級別中嫌蚤,是解決了幻讀的讀問題的。參見下圖

讀問題解決了断傲,根據(jù)MVCC的定義脱吱,并發(fā)提交數(shù)據(jù)時會出現(xiàn)沖突,那么沖突時如何解決呢认罩?我們再來看看InnoDB中RR級別對于寫數(shù)據(jù)的處理箱蝠。

####“讀”與“讀”的區(qū)別

可能有讀者會疑惑,事務(wù)的隔離級別其實都是對于讀數(shù)據(jù)的定義猜年,但到了這里抡锈,就被拆成了讀和寫兩個模塊來講解疾忍。這主要是因為MySQL中的讀乔外,和事務(wù)隔離級別中的讀,是不一樣的一罩。

我們且看杨幼,在RR級別中,通過MVCC機(jī)制聂渊,雖然讓數(shù)據(jù)變得可重復(fù)讀差购,但我們讀到的數(shù)據(jù)可能是歷史數(shù)據(jù),是不及時的數(shù)據(jù)汉嗽,不是數(shù)據(jù)庫當(dāng)前的數(shù)據(jù)欲逃!這在一些對于數(shù)據(jù)的時效特別敏感的業(yè)務(wù)中,就很可能出問題饼暑。

對于這種讀取歷史數(shù)據(jù)的方式稳析,我們叫它快照讀 (snapshot read),而讀取數(shù)據(jù)庫當(dāng)前版本數(shù)據(jù)的方式弓叛,叫當(dāng)前讀 (current read)彰居。很顯然,在MVCC中:

快照讀:就是select

select * from table ....;

當(dāng)前讀:特殊的讀操作撰筷,插入/更新/刪除操作陈惰,屬于當(dāng)前讀,處理的都是當(dāng)前的數(shù)據(jù)毕籽,需要加鎖抬闯。

select * from table where ? lock in share mode;

select * from table where ? for update;

insert;

update ;

delete;

事務(wù)的隔離級別實際上都是定義了當(dāng)前讀的級別井辆,MySQL為了減少鎖處理(包括等待其它鎖)的時間,提升并發(fā)能力溶握,引入了快照讀的概念掘剪,使得select不用加鎖。而update奈虾、insert這些“當(dāng)前讀”夺谁,就需要另外的模塊來解決了。

###寫("當(dāng)前讀")

事務(wù)的隔離級別中雖然只定義了讀數(shù)據(jù)的要求肉微,實際上這也可以說是寫數(shù)據(jù)的要求匾鸥。上文的“讀”,實際是講的快照讀碉纳;而這里說的“寫”就是當(dāng)前讀了勿负。

為了解決當(dāng)前讀中的幻讀問題,MySQL事務(wù)使用了Next-Key鎖劳曹。

####Next-Key鎖

Next-Key鎖是行鎖和GAP(間隙鎖)的合并奴愉,行鎖上文已經(jīng)介紹了,接下來說下GAP間隙鎖铁孵。

行鎖可以防止不同事務(wù)版本的數(shù)據(jù)修改提交時造成數(shù)據(jù)沖突的情況锭硼。但如何避免別的事務(wù)插入數(shù)據(jù)就成了問題。我們可以看看RR級別和RC級別的對比

RC級別:

事務(wù)A事務(wù)B

begin;begin;

select id,class_name,teacher_id from class_teacher where teacher_id=30;

idclass_nameteacher_id

2初三二班30

update class_teacher set class_name='初三四班' where teacher_id=30;

insert into class_teacher values (null,'初三二班',30);

commit;

select id,class_name,teacher_id from class_teacher where teacher_id=30;

idclass_nameteacher_id

2初三四班30

10初三二班30

RR級別:

事務(wù)A事務(wù)B

begin;begin;

select id,class_name,teacher_id from class_teacher where teacher_id=30;

idclass_nameteacher_id

2初三二班30

update class_teacher set class_name='初三四班' where teacher_id=30;

insert into class_teacher values (null,'初三二班',30);

waiting....

select id,class_name,teacher_id from class_teacher where teacher_id=30;

idclass_nameteacher_id

2初三四班30

commit;事務(wù)Acommit后蜕劝,事務(wù)B的insert執(zhí)行檀头。

通過對比我們可以發(fā)現(xiàn),在RC級別中岖沛,事務(wù)A修改了所有teacher_id=30的數(shù)據(jù)暑始,但是當(dāng)事務(wù)Binsert進(jìn)新數(shù)據(jù)后,事務(wù)A發(fā)現(xiàn)莫名其妙多了一行teacher_id=30的數(shù)據(jù)婴削,而且沒有被之前的update語句所修改廊镜,這就是“當(dāng)前讀”的幻讀。

RR級別中唉俗,事務(wù)A在update后加鎖嗤朴,事務(wù)B無法插入新數(shù)據(jù),這樣事務(wù)A在update前后讀的數(shù)據(jù)保持一致互躬,避免了幻讀播赁。這個鎖,就是Gap鎖吼渡。

MySQL是這么實現(xiàn)的:

在class_teacher這張表中容为,teacher_id是個索引,那么它就會維護(hù)一套B+樹的數(shù)據(jù)關(guān)系,為了簡化坎背,我們用鏈表結(jié)構(gòu)來表達(dá)(實際上是個樹形結(jié)構(gòu)替劈,但原理相同)

如圖所示,InnoDB使用的是聚集索引得滤,teacher_id身為二級索引陨献,就要維護(hù)一個索引字段和主鍵id的樹狀結(jié)構(gòu)(這里用鏈表形式表現(xiàn)),并保持順序排列懂更。

Innodb將這段數(shù)據(jù)分成幾個個區(qū)間

(negative infinity, 5],

(5,30],

(30,positive infinity)眨业;

update class_teacher set class_name='初三四班' where teacher_id=30;不僅用行鎖,鎖住了相應(yīng)的數(shù)據(jù)行沮协;同時也在兩邊的區(qū)間龄捡,(5,30]和(30,positive infinity)慷暂,都加入了gap鎖聘殖。這樣事務(wù)B就無法在這個兩個區(qū)間insert進(jìn)新數(shù)據(jù)。

受限于這種實現(xiàn)方式行瑞,Innodb很多時候會鎖住不需要鎖的區(qū)間奸腺。如下所示:

事務(wù)A事務(wù)B事務(wù)C

begin;begin;begin;

select id,class_name,teacher_id from class_teacher;

idclass_nameteacher_id

1初三一班5

2初三二班30

update class_teacher set class_name='初一一班' where teacher_id=20;

insert into class_teacher values (null,'初三五班',10);

waiting .....

insert into class_teacher values (null,'初三五班',40);

commit;事務(wù)A commit之后,這條語句才插入成功commit;

commit;

update的teacher_id=20是在(5血久,30]區(qū)間突照,即使沒有修改任何數(shù)據(jù),Innodb也會在這個區(qū)間加gap鎖洋魂,而其它區(qū)間不會影響绷旗,事務(wù)C正常插入。

如果使用的是沒有索引的字段副砍,比如update class_teacher set teacher_id=7 where class_name='初三八班(即使沒有匹配到任何數(shù)據(jù))',那么會給全表加入gap鎖。同時庄岖,它不能像上文中行鎖一樣經(jīng)過MySQL Server過濾自動解除不滿足條件的鎖豁翎,因為沒有索引,則這些字段也就沒有排序隅忿,也就沒有區(qū)間心剥。除非該事務(wù)提交,否則其它事務(wù)無法插入任何數(shù)據(jù)背桐。

行鎖防止別的事務(wù)修改或刪除优烧,GAP鎖防止別的事務(wù)新增,行鎖和GAP鎖結(jié)合形成的的Next-Key鎖共同解決了RR級別在寫數(shù)據(jù)時的幻讀問題链峭。

###Serializable

這個級別很簡單畦娄,讀加共享鎖,寫加排他鎖,讀寫互斥熙卡。使用的悲觀鎖的理論杖刷,實現(xiàn)簡單,數(shù)據(jù)更加安全驳癌,但是并發(fā)能力非常差滑燃。如果你的業(yè)務(wù)并發(fā)的特別少或者沒有并發(fā),同時又要求數(shù)據(jù)及時可靠的話颓鲜,可以使用這種模式表窘。

這里要吐槽一句,不要看到select就說不會加鎖了甜滨,在Serializable這個級別蚊丐,還是會加鎖的!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末艳吠,一起剝皮案震驚了整個濱河市麦备,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌昭娩,老刑警劉巖凛篙,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異栏渺,居然都是意外死亡呛梆,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門磕诊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來填物,“玉大人,你說我怎么就攤上這事霎终≈突牵” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵莱褒,是天一觀的道長击困。 經(jīng)常有香客問我,道長广凸,這世上最難降的妖魔是什么阅茶? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮谅海,結(jié)果婚禮上脸哀,老公的妹妹穿的比我還像新娘。我一直安慰自己扭吁,他們只是感情好撞蜂,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布盲镶。 她就那樣靜靜地躺著,像睡著了一般谅摄。 火紅的嫁衣襯著肌膚如雪徒河。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天送漠,我揣著相機(jī)與錄音顽照,去河邊找鬼。 笑死闽寡,一個胖子當(dāng)著我的面吹牛代兵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播爷狈,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼植影,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涎永?” 一聲冷哼從身側(cè)響起思币,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎羡微,沒想到半個月后谷饿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡妈倔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年博投,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盯蝴。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡毅哗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出捧挺,到底是詐尸還是另有隱情虑绵,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布松忍,位于F島的核電站蒸殿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鸣峭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一酥艳、第九天 我趴在偏房一處隱蔽的房頂上張望摊溶。 院中可真熱鬧,春花似錦充石、人聲如沸莫换。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拉岁。三九已至坷剧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間喊暖,已是汗流浹背惫企。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留陵叽,地道東北人狞尔。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像巩掺,于是被迫代替她去往敵國和親偏序。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355