Innodb中的事務隔離級別和鎖的關系

轉載自: https://tech.meituan.com/innodb-lock.html

前言:

我們都知道事務的幾種性質势篡,數(shù)據(jù)庫為了維護這些性質,尤其是一致性和隔離性祟蚀,一般使用加鎖這種方式可霎。同時數(shù)據(jù)庫又是個高并發(fā)的應用,同一時間會有大量的并發(fā)訪問密强,如果加鎖過度茅郎,會極大的降低并發(fā)處理能力。所以對于加鎖的處理或渤,可以說就是數(shù)據(jù)庫對于事務處理的精髓所在系冗。這里通過分析MySQL中InnoDB引擎的加鎖機制,來拋磚引玉薪鹦,讓讀者更好的理解掌敬,在事務處理中數(shù)據(jù)庫到底做了什么惯豆。

一次封鎖or兩段鎖?

因為有大量的并發(fā)訪問奔害,為了預防死鎖楷兽,一般應用中推薦使用一次封鎖法,就是在方法的開始階段华临,已經預先知道會用到哪些數(shù)據(jù)芯杀,然后全部鎖住,在方法運行之后雅潭,再全部解鎖揭厚。這種方式可以有效的避免循環(huán)死鎖,但在數(shù)據(jù)庫中卻不適用扶供,因為在事務開始階段棋弥,數(shù)據(jù)庫并不知道會用到哪些數(shù)據(jù)。
數(shù)據(jù)庫遵循的是兩段鎖協(xié)議诚欠,將事務分成兩個階段顽染,加鎖階段和解鎖階段(所以叫兩段鎖)

  • 加鎖階段:在該階段可以進行加鎖操作。在對任何數(shù)據(jù)進行讀操作之前要申請并獲得S鎖(共享鎖轰绵,其它事務可以繼續(xù)加共享鎖粉寞,但不能加排它鎖),在進行寫操作之前要申請并獲得X鎖(排它鎖左腔,其它事務不能再獲得任何鎖)唧垦。加鎖不成功,則事務進入等待狀態(tài)液样,直到加鎖成功才繼續(xù)執(zhí)行振亮。
  • 解鎖階段:當事務釋放了一個封鎖以后,事務進入解鎖階段鞭莽,在該階段只能進行解鎖操作不能再進行加鎖操作坊秸。
事務 加鎖/解鎖處理
begin;
insert into test ..... 加insert對應的鎖
update test set... 加update對應的鎖
delete from test .... 加delete對應的鎖
commit; 事務提交時澎怒,同時釋放insert褒搔、update、delete對應的鎖

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

事務中的加鎖方式

事務的四種隔離級別

在數(shù)據(jù)庫操作中惧辈,為了有效保證并發(fā)讀取數(shù)據(jù)的正確性琳状,提出的事務隔離級別。我們的數(shù)據(jù)庫鎖盒齿,也是為了構建這些隔離級別存在的念逞。

隔離級別 臟讀(Dirty Read) 不可重復讀(NonRepeatable Read) 幻讀(Phantom Read)
未提交讀(Read uncommitted) 可能 可能 可能
已提交讀(Read committed) 不可能 可能 可能
可重復讀(Repeatable read) 不可能 不可能 可能
可串行化(Serializable ) 不可能 不可能 不可能
  • 未提交讀(Read Uncommitted):允許臟讀生宛,也就是可能讀取到其他會話中未提交事務修改的數(shù)據(jù)
  • 提交讀(Read Committed):只能讀取到已經提交的數(shù)據(jù)。Oracle等多數(shù)數(shù)據(jù)庫默認都是該級別 (不重復讀)
  • 可重復讀(Repeated Read):可重復讀肮柜。在同一個事務內的查詢都是事務開始時刻一致的,InnoDB默認級別倒彰。在SQL標準中审洞,該隔離級別消除了不可重復讀,但是還存在幻象讀
  • 串行讀(Serializable):完全串行化的讀待讳,每次讀都需要獲得表級共享鎖芒澜,讀寫相互都會阻塞

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

MySQL中鎖的種類

MySQL中鎖的種類很多琳彩,有常見的表鎖和行鎖誊酌,也有新加入的Metadata Lock等等,表鎖是對一整張表加鎖,雖然可分為讀鎖和寫鎖露乏,但畢竟是鎖住整張表碧浊,會導致并發(fā)能力下降,一般是做ddl處理時使用瘟仿。

行鎖則是鎖住數(shù)據(jù)行箱锐,這種加鎖方法比較復雜,但是由于只鎖住有限的數(shù)據(jù)劳较,對于其它數(shù)據(jù)不加限制驹止,所以并發(fā)能力強,MySQL一般都是用行鎖來處理并發(fā)事務观蜗。這里主要討論的也就是行鎖臊恋。

Read Committed(讀取提交內容)

在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默認是使用的RR級別毙替,所以我們先要將該session開啟成RC級別岸售,并且設置binlog的模式

SET session transaction isolation level read committed;
SET SESSION binlog_format = 'ROW';(或者是MIXED)

事務A 事務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ā)過程中的修改沖突,事務A中MySQL給teacher_id=1的數(shù)據(jù)行加鎖厂画,并一直不commit(釋放鎖)凸丸,那么事務B也就一直拿不到該行鎖,wait直到超時袱院。

這時我們要注意到屎慢,teacher_id是有索引的瞭稼,如果是沒有索引的class_name呢?update class_teacher set teacher_id=3 where class_name = '初三一班';
那么MySQL會給整張表的所有數(shù)據(jù)行的加行鎖腻惠。這里聽起來有點不可思議环肘,但是當sql運行的過程中,MySQL并不知道哪些數(shù)據(jù)行是 class_name = '初三一班'的(沒有索引嘛)集灌,如果一個條件無法通過索引快速過濾悔雹,存儲引擎層面就會將所有記錄加鎖后返回,再由MySQL Server層進行過濾欣喧。

但在實際使用過程當中腌零,MySQL做了一些改進,在MySQL Server過濾條件唆阿,發(fā)現(xiàn)不滿足后益涧,會調用unlock_row方法,把不滿足條件的記錄釋放鎖 (違背了二段鎖協(xié)議的約束)驯鳖。這樣做闲询,保證了最后只會持有滿足條件記錄上的鎖,但是每條記錄的加鎖操作還是不能省略的浅辙∴诹眩可見即使是MySQL,為了效率也是會違反規(guī)范的摔握。(參見《高性能MySQL》中文第三版p181)

這種情況同樣適用于MySQL的默認隔離級別RR寄狼。所以對一個數(shù)據(jù)量很大的表做批量修改的時候,如果無法使用相應的索引氨淌,MySQL Server過濾數(shù)據(jù)的的時候特別慢泊愧,就會出現(xiàn)雖然沒有修改某些行的數(shù)據(jù),但是它們還是被鎖住了的現(xiàn)象盛正。

Repeatable Read(可重讀)

這是MySQL中InnoDB默認的隔離級別删咱。我們姑且分“讀”和“寫”兩個模塊來講解。

讀就是可重讀豪筝,可重讀這個概念是一事務的多個實例在并發(fā)讀取數(shù)據(jù)時痰滋,會看到同樣的數(shù)據(jù)行,有點抽象续崖,我們來看一下效果敲街。

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

| 事務A | 事務B |
| begin; |

begin;

|
|

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

| id | class_name | teacher_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;

| id | class_name | teacher_id |
| 1 | 初三三班 | 1 |
| 2 | 初三一班 | 1 |

讀到了事務B修改的數(shù)據(jù),和第一次查詢的結果不一樣严望,是不可重讀的多艇。

| |
| commit; | |

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

| 事務A | 事務B | 事務C |
| begin; |

begin;

|

begin;

|
|

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

| id | class_name | teacher_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;

| id | class_name | teacher_id |
| 1 | 初三二班 | 1 |
| 2 | 初三一班 | 1 |

沒有讀到事務B修改的數(shù)據(jù)姆涩,和第一次sql讀取的一樣挽拂,是可重復讀的。

沒有讀到事務C新添加的數(shù)據(jù)骨饿。

| | |
| commit; | | |

我們注意到亏栈,當teacher_id=1時,事務A先做了一次讀取样刷,事務B中間修改了id=1的數(shù)據(jù),并commit之后览爵,事務A第二次讀到的數(shù)據(jù)和第一次完全相同置鼻。所以說它是可重讀的。那么MySQL是怎么做到的呢蜓竹?這里姑且賣個關子箕母,我們往下看。

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

很多人容易搞混不可重復讀和幻讀俱济,確實這兩者有些相似嘶是。但不可重復讀重點在于update和delete,而幻讀的重點在于insert蛛碌。

如果使用鎖機制來實現(xiàn)這兩種隔離級別聂喇,在可重復讀中,該sql第一次讀取到數(shù)據(jù)后蔚携,就將這些數(shù)據(jù)加鎖希太,其它事務無法修改這些數(shù)據(jù),就可以實現(xiàn)可重復讀了酝蜒。但這種方法卻無法鎖住insert的數(shù)據(jù)誊辉,所以當事務A先前讀取了數(shù)據(jù),或者修改了全部數(shù)據(jù)亡脑,事務B還是可以insert數(shù)據(jù)提交堕澄,這時事務A就會發(fā)現(xiàn)莫名其妙多了一條之前沒有的數(shù)據(jù),這就是幻讀霉咨,不能通過行鎖來避免蛙紫。需要Serializable隔離級別 ,讀用讀鎖途戒,寫用寫鎖惊来,讀鎖和寫鎖互斥,這么做可以有效的避免幻讀棺滞、不可重復讀裁蚁、臟讀等問題矢渊,但會極大的降低數(shù)據(jù)庫的并發(fā)能力。

所以說不可重復讀和幻讀最大的區(qū)別枉证,就在于如何通過鎖機制來解決他們產生的問題矮男。

上文說的,是使用悲觀鎖機制來處理這兩種問題室谚,但是MySQL毡鉴、ORACLE、PostgreSQL等成熟的數(shù)據(jù)庫秒赤,出于性能考慮猪瞬,都是使用了以樂觀鎖為理論基礎的MVCC(多版本并發(fā)控制)來避免這兩種問題。

悲觀鎖和樂觀鎖####

  • 悲觀鎖

正如其名入篮,它指的是對數(shù)據(jù)被外界(包括本系統(tǒng)當前的其他事務陈瘦,以及來自外部系統(tǒng)的事務處理)修改持保守態(tài)度,因此潮售,在整個數(shù)據(jù)處理過程中痊项,將數(shù)據(jù)處于鎖定狀態(tài)。悲觀鎖的實現(xiàn)酥诽,往往依靠數(shù)據(jù)庫提供的鎖機制(也只有數(shù)據(jù)庫層提供的鎖機制才能真正保證數(shù)據(jù)訪問的排他性鞍泉,否則,即使在本系統(tǒng)中實現(xiàn)了加鎖機制肮帐,也無法保證外部系統(tǒng)不會修改數(shù)據(jù))咖驮。

在悲觀鎖的情況下,為了保證事務的隔離性训枢,就需要一致性鎖定讀游沿。讀取數(shù)據(jù)時給加鎖,其它事務無法修改這些數(shù)據(jù)肮砾。修改刪除數(shù)據(jù)時也要加鎖诀黍,其它事務無法讀取這些數(shù)據(jù)。

  • 樂觀鎖

相對悲觀鎖而言仗处,樂觀鎖機制采取了更加寬松的加鎖機制眯勾。悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫的鎖機制實現(xiàn),以保證操作最大程度的獨占性婆誓。但隨之而來的就是數(shù)據(jù)庫性能的大量開銷吃环,特別是對長事務而言,這樣的開銷往往無法承受洋幻。

而樂觀鎖機制在一定程度上解決了這個問題郁轻。樂觀鎖,大多是基于數(shù)據(jù)版本( Version )記錄機制實現(xiàn)。何謂數(shù)據(jù)版本好唯?即為數(shù)據(jù)增加一個版本標識竭沫,在基于數(shù)據(jù)庫表的版本解決方案中,一般是通過為數(shù)據(jù)庫表增加一個 “version” 字段來實現(xiàn)骑篙。讀取出數(shù)據(jù)時蜕提,將此版本號一同讀出,之后更新時靶端,對此版本號加一谎势。此時,將提交數(shù)據(jù)的版本數(shù)據(jù)與數(shù)據(jù)庫表對應記錄的當前版本信息進行比對杨名,如果提交的數(shù)據(jù)版本號大于數(shù)據(jù)庫表當前版本號脏榆,則予以更新,否則認為是過期數(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ù)何時過期(或者被刪除)骂因。 在實際操作中炎咖,存儲的并不是時間,而是事務的版本號寒波,每開啟一個新事務乘盼,事務的版本號就會遞增。 在可重讀Repeatable reads事務隔離級別下:

  • SELECT時俄烁,讀取創(chuàng)建版本號<=當前事務版本號绸栅,刪除版本號為空或>當前事務版本號。
  • INSERT時页屠,保存當前事務版本號為行的創(chuàng)建版本號
  • DELETE時粹胯,保存當前事務版本號為行的刪除版本號
  • UPDATE時,插入一條新紀錄辰企,保存當前事務版本號為行創(chuàng)建版本號风纠,同時保存當前事務版本號到原來刪除的行

通過MVCC,雖然每行記錄都需要額外的存儲空間牢贸,更多的行檢查工作以及一些額外的維護工作竹观,但可以減少鎖的使用,大多數(shù)讀操作都不用加鎖,讀數(shù)據(jù)操作很簡單臭增,性能很好懂酱,并且也能保證只會讀取到符合標準的行,也只鎖住必要行速址。

我們不管從數(shù)據(jù)庫方面的教課書中學到玩焰,還是從網絡上看到,大都是上文中事務的四種隔離級別這一模塊列出的意思芍锚,RR級別是可重復讀的昔园,但無法解決幻讀,而只有在Serializable級別才能解決幻讀并炮。于是我就加了一個事務C來展示效果默刚。在事務C中添加了一條teacher_id=1的數(shù)據(jù)commit,RR級別中應該會有幻讀現(xiàn)象逃魄,事務A在查詢teacher_id=1的數(shù)據(jù)時會讀到事務C新加的數(shù)據(jù)荤西。但是測試后發(fā)現(xiàn),在MySQL中是不存在這種情況的伍俘,在事務C提交后邪锌,事務A還是不會讀到這條數(shù)據(jù)“可見在MySQL的RR級別中觅丰,是解決了幻讀的讀問題的。參見下圖

innodb_lock_1

讀問題解決了妨退,根據(jù)MVCC的定義妇萄,并發(fā)提交數(shù)據(jù)時會出現(xiàn)沖突,那么沖突時如何解決呢咬荷?我們再來看看InnoDB中RR級別對于寫數(shù)據(jù)的處理冠句。

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

可能有讀者會疑惑,事務的隔離級別其實都是對于讀數(shù)據(jù)的定義幸乒,但到了這里懦底,就被拆成了讀和寫兩個模塊來講解。這主要是因為MySQL中的讀罕扎,和事務隔離級別中的讀聚唐,是不一樣的。

我們且看壳影,在RR級別中拱层,通過MVCC機制,雖然讓數(shù)據(jù)變得可重復讀宴咧,但我們讀到的數(shù)據(jù)可能是歷史數(shù)據(jù)根灯,是不及時的數(shù)據(jù),不是數(shù)據(jù)庫當前的數(shù)據(jù)!這在一些對于數(shù)據(jù)的時效特別敏感的業(yè)務中烙肺,就很可能出問題纳猪。

對于這種讀取歷史數(shù)據(jù)的方式,我們叫它快照讀 (snapshot read)桃笙,而讀取數(shù)據(jù)庫當前版本數(shù)據(jù)的方式氏堤,叫當前讀 (current read)。很顯然搏明,在MVCC中:

  • 快照讀:就是select
    • select * from table ....;
  • 當前讀:特殊的讀操作鼠锈,插入/更新/刪除操作,屬于當前讀星著,處理的都是當前的數(shù)據(jù)购笆,需要加鎖。
    • select * from table where ? lock in share mode;
    • select * from table where ? for update;
    • insert;
    • update ;
    • delete;

事務的隔離級別實際上都是定義了當前讀的級別虚循,MySQL為了減少鎖處理(包括等待其它鎖)的時間同欠,提升并發(fā)能力,引入了快照讀的概念横缔,使得select不用加鎖铺遂。而update、insert這些“當前讀”茎刚,就需要另外的模塊來解決了襟锐。

寫("當前讀")

事務的隔離級別中雖然只定義了讀數(shù)據(jù)的要求,實際上這也可以說是寫數(shù)據(jù)的要求斗蒋。上文的“讀”捌斧,實際是講的快照讀笛质;而這里說的“寫”就是當前讀了泉沾。
為了解決當前讀中的幻讀問題,MySQL事務使用了Next-Key鎖妇押。

Next-Key鎖

Next-Key鎖是行鎖和GAP(間隙鎖)的合并跷究,行鎖上文已經介紹了,接下來說下GAP間隙鎖敲霍。

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

RC級別:

| 事務A | 事務B |
| begin; |

begin;

|
|

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

| id | class_name | teacher_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;

| id | class_name | teacher_id |
| 2 | 初三四班 | 30 |
| 10 | 初三二班 | 30 |

| |

RR級別:

| 事務A | 事務B |
| begin; |

begin;

|
|

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

| id | class_name | teacher_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;

| id | class_name | teacher_id |
| 2 | 初三四班 | 30 |

| |
| commit; | 事務Acommit后肩杈,事務B的insert執(zhí)行柴我。 |

通過對比我們可以發(fā)現(xiàn),在RC級別中扩然,事務A修改了所有teacher_id=30的數(shù)據(jù)艘儒,但是當事務Binsert進新數(shù)據(jù)后,事務A發(fā)現(xiàn)莫名其妙多了一行teacher_id=30的數(shù)據(jù),而且沒有被之前的update語句所修改界睁,這就是“當前讀”的幻讀觉增。

RR級別中,事務A在update后加鎖翻斟,事務B無法插入新數(shù)據(jù)逾礁,這樣事務A在update前后讀的數(shù)據(jù)保持一致,避免了幻讀访惜。這個鎖嘹履,就是Gap鎖。

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

在class_teacher這張表中债热,teacher_id是個索引植捎,那么它就會維護一套B+樹的數(shù)據(jù)關系,為了簡化阳柔,我們用鏈表結構來表達(實際上是個樹形結構焰枢,但原理相同)

innodb_lock_2

如圖所示,InnoDB使用的是聚集索引舌剂,teacher_id身為二級索引济锄,就要維護一個索引字段和主鍵id的樹狀結構(這里用鏈表形式表現(xiàn)),并保持順序排列霍转。

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

  • (negative infinity, 5],
  • (5,30],
  • (30,positive infinity)荐绝;

update class_teacher set class_name='初三四班' where teacher_id=30;不僅用行鎖,鎖住了相應的數(shù)據(jù)行避消;同時也在兩邊的區(qū)間低滩,(5,30]和(30,positive infinity)岩喷,都加入了gap鎖恕沫。這樣事務B就無法在這個兩個區(qū)間insert進新數(shù)據(jù)。

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

| 事務A | 事務B | 事務C |
| begin; | begin; | begin; |
|

select id,class_name,teacher_id from class_teacher;

| id | class_name | teacher_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; | 事務A commit之后,這條語句才插入成功 | commit; |
| | commit; | |

update的teacher_id=20是在(5偷霉,30]區(qū)間迄委,即使沒有修改任何數(shù)據(jù),Innodb也會在這個區(qū)間加gap鎖类少,而其它區(qū)間不會影響叙身,事務C正常插入。

如果使用的是沒有索引的字段硫狞,比如update class_teacher set teacher_id=7 where class_name='初三八班(即使沒有匹配到任何數(shù)據(jù))',那么會給全表加入gap鎖信轿。同時赞警,它不能像上文中行鎖一樣經過MySQL Server過濾自動解除不滿足條件的鎖,因為沒有索引虏两,則這些字段也就沒有排序愧旦,也就沒有區(qū)間。除非該事務提交定罢,否則其它事務無法插入任何數(shù)據(jù)笤虫。

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

Serializable

這個級別很簡單,讀加共享鎖惠况,寫加排他鎖遭庶,讀寫互斥。使用的悲觀鎖的理論稠屠,實現(xiàn)簡單峦睡,數(shù)據(jù)更加安全,但是并發(fā)能力非常差权埠。如果你的業(yè)務并發(fā)的特別少或者沒有并發(fā)榨了,同時又要求數(shù)據(jù)及時可靠的話,可以使用這種模式攘蔽。

這里要吐槽一句龙屉,不要看到select就說不會加鎖了,在Serializable這個級別满俗,還是會加鎖的转捕!

參考資料

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市唆垃,隨后出現(xiàn)的幾起案子五芝,更是在濱河造成了極大的恐慌,老刑警劉巖降盹,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件与柑,死亡現(xiàn)場離奇詭異谤辜,居然都是意外死亡蓄坏,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門丑念,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涡戳,“玉大人,你說我怎么就攤上這事脯倚∮嬲茫” “怎么了嵌屎?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長恍涂。 經常有香客問我宝惰,道長,這世上最難降的妖魔是什么再沧? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任尼夺,我火速辦了婚禮,結果婚禮上炒瘸,老公的妹妹穿的比我還像新娘淤堵。我一直安慰自己,他們只是感情好顷扩,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布拐邪。 她就那樣靜靜地躺著,像睡著了一般隘截。 火紅的嫁衣襯著肌膚如雪扎阶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天婶芭,我揣著相機與錄音乘陪,去河邊找鬼。 笑死雕擂,一個胖子當著我的面吹牛啡邑,可吹牛的內容都是我干的。 我是一名探鬼主播井赌,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼谤逼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了仇穗?” 一聲冷哼從身側響起流部,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎纹坐,沒想到半個月后枝冀,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年走贪,在試婚紗的時候發(fā)現(xiàn)自己被綠了枚赡。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡绒障,死狀恐怖,靈堂內的尸體忽然破棺而出捍歪,到底是詐尸還是另有隱情户辱,我是刑警寧澤鸵钝,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站庐镐,受9級特大地震影響恩商,放射性物質發(fā)生泄漏。R本人自食惡果不足惜必逆,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一痕届、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧末患,春花似錦研叫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至探橱,卻和暖如春申屹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背隧膏。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工哗讥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人胞枕。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓杆煞,卻偏偏與公主長得像,于是被迫代替她去往敵國和親腐泻。 傳聞我的和親對象是個殘疾皇子决乎,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內容