MySQL的鎖機(jī)制和加鎖原理

首先對(duì)mysql鎖進(jìn)行劃分:

  • 按照鎖的粒度劃分:行鎖、表鎖昧捷、頁(yè)鎖
  • 按照鎖的使用方式劃分:共享鎖刻炒、排它鎖(悲觀鎖的一種實(shí)現(xiàn))
  • 還有兩種思想上的鎖:悲觀鎖、樂(lè)觀鎖栖榨。
  • InnoDB中有幾種行級(jí)鎖類型:Record Lock昆汹、Gap Lock、Next-key Lock
    1. Record Lock:在索引記錄上加鎖
    2. Gap Lock:間隙鎖
    3. Next-key Lock:Record Lock+Gap Lock

行鎖

  • 行級(jí)鎖是Mysql中鎖定粒度最細(xì)的一種鎖婴栽,表示只針對(duì)當(dāng)前操作的行進(jìn)行加鎖满粗。
  • 行級(jí)鎖能大大減少數(shù)據(jù)庫(kù)操作的沖突。其加鎖粒度最小愚争,但加鎖的開銷也最大映皆。有可能會(huì)出現(xiàn)死鎖的情況。
  • 行級(jí)鎖按照使用方式分為:共享鎖排他鎖轰枝。

共享鎖用法(S鎖 / 讀鎖):

  • 若事務(wù)T捅彻,對(duì)數(shù)據(jù)對(duì)象A加上S鎖,則事務(wù)T可以讀A但不能修改A鞍陨,其他事務(wù)只能再對(duì)A加S鎖步淹,而不能加X(jué)鎖(排他鎖),直到T釋放A上的S鎖诚撵。這保證了其他事務(wù)可以讀A贤旷,但在T釋放A上的S鎖之前不能對(duì)A做任何修改。
select ... lock in share mode;
  • 共享鎖就是允許多個(gè)線程同時(shí)獲取一個(gè)鎖砾脑,一個(gè)鎖可以同時(shí)被多個(gè)線程擁有幼驶。

排它鎖用法(X鎖 / 寫鎖):

  • 若事務(wù)T,對(duì)數(shù)據(jù)對(duì)象A加上X鎖韧衣,事務(wù)T可以讀A也可以修改A盅藻,其他事務(wù)不能再對(duì)A加任何鎖,直到T釋放A上的鎖畅铭。這保證了其他事務(wù)在T釋放A上的鎖之前不能再讀取和修改A氏淑。
select ... for update
  • 排它鎖,也稱作獨(dú)占鎖硕噩,一個(gè)鎖在某一時(shí)刻只能被一個(gè)線程占有假残,其它線程必須等待鎖被釋放之后才可能獲取到鎖

表鎖

  • 表級(jí)鎖是mysql鎖中粒度最大的一種鎖,表示當(dāng)前的操作對(duì)整張表加鎖辉懒。
  • 資源開銷比行鎖少阳惹,不會(huì)出現(xiàn)死鎖的情況,但是發(fā)生鎖沖突的概率很大眶俩。
  • 被大部分的mysql引擎支持莹汤,MyISAM和InnoDB都支持表級(jí)鎖,但是InnoDB默認(rèn)的是行級(jí)鎖颠印。
注意:表鎖纲岭,講的是鎖的顆粒度,共享鎖线罕、排他鎖講的是使用方式止潮。

共享鎖用法:

  • 釋義在上面行鎖講過(guò),只是用法不同钞楼。
LOCK TABLE table_name [ AS alias_name ] READ

排它鎖用法:

  • 釋義在上面行鎖講過(guò)喇闸,只是用法不同。
LOCK TABLE table_name [AS alias_name][ LOW_PRIORITY ] WRITE

解鎖用法:

unlock tables;

頁(yè)鎖

  • 頁(yè)級(jí)鎖是MySQL中鎖定粒度介于行級(jí)鎖和表級(jí)鎖中間的一種鎖窿凤。
  • 表級(jí)鎖速度快仅偎,但沖突多跨蟹,行級(jí)沖突少雳殊,但速度慢。所以取了折衷的頁(yè)級(jí)窗轩,一次鎖定相鄰的一組記錄夯秃。
  • BDB支持頁(yè)級(jí)鎖。
    1. MySQL 一種數(shù)據(jù)庫(kù)存儲(chǔ)引擎痢艺。
    2. BDB引擎:Sleepycat Software給MySQL提供Berkeley DB事務(wù)性存儲(chǔ)引擎仓洼。這個(gè)存儲(chǔ)引擎典型被簡(jiǎn)稱為BDB。
    3. BDB存儲(chǔ)引擎的支持包括在MySQL源碼分發(fā)版里堤舒,在MySQL-Max二進(jìn)制分發(fā)版里被激活色建。因此,mysqld和mysqld-nt是不支持BDB數(shù)據(jù)庫(kù)引擎的舌缤,若要支持應(yīng)選擇mysqld-max或mysqld-max-nt為服務(wù)箕戳。
    4. 要查看當(dāng)前數(shù)據(jù)庫(kù)支持哪些引擎,可以使用show engines命令国撵。

樂(lè)觀鎖和悲觀鎖(思想設(shè)計(jì)上的鎖)

  • 在數(shù)據(jù)庫(kù)的鎖機(jī)制中介紹過(guò)陵吸,數(shù)據(jù)庫(kù)管理系統(tǒng)(DBMS)中的并發(fā)控制的任務(wù)是確保在多個(gè)事務(wù)同時(shí)存取數(shù)據(jù)庫(kù)中同一數(shù)據(jù)時(shí)不破壞事務(wù)的隔離性和統(tǒng)一性以及數(shù)據(jù)庫(kù)的統(tǒng)一性。
  • 樂(lè)觀并發(fā)控制(樂(lè)觀鎖)和悲觀并發(fā)控制(悲觀鎖)是并發(fā)控制主要采用的技術(shù)手段介牙。
  • 無(wú)論是悲觀鎖還是樂(lè)觀鎖壮虫,都是人們定義出來(lái)的概念,可以認(rèn)為是一種思想环础。其實(shí)不僅僅是關(guān)系型數(shù)據(jù)庫(kù)系統(tǒng)中有樂(lè)觀鎖和悲觀鎖的概念囚似,像memcache剩拢、hibernate、tair等都有類似的概念谆构。
  • 針對(duì)于不同的業(yè)務(wù)場(chǎng)景裸扶,應(yīng)該選用不同的并發(fā)控制方式。所以搬素,不要把樂(lè)觀并發(fā)控制和悲觀并發(fā)控制狹義的理解為DBMS中的概念呵晨,更不要把他們和數(shù)據(jù)庫(kù)中提供的鎖機(jī)制(行鎖、表鎖熬尺、排他鎖摸屠、共享鎖)混為一談。其實(shí)粱哼,在DBMS中季二,悲觀鎖正是利用數(shù)據(jù)庫(kù)本身提供的鎖機(jī)制來(lái)實(shí)現(xiàn)的。
  • 市場(chǎng)上比較流行的數(shù)據(jù)庫(kù)管理系統(tǒng)產(chǎn)品主要是:Oracle揭措、IBM胯舷、MicrosoftSybaseMysql 等公司的產(chǎn)品绊含。

悲觀鎖

在關(guān)系數(shù)據(jù)庫(kù)管理系統(tǒng)里桑嘶,悲觀并發(fā)控制(又名“悲觀鎖”,Pessimistic Concurrency Control躬充,縮寫“PCC”)是一種并發(fā)控制的方法逃顶。它可以阻止一個(gè)事務(wù)以影響其他用戶的方式來(lái)修改數(shù)據(jù)。如果一個(gè)事務(wù)執(zhí)行的操作對(duì)某行數(shù)據(jù)應(yīng)用了鎖充甚,那只有當(dāng)這個(gè)事務(wù)把鎖釋放以政,其他事務(wù)才能夠執(zhí)行與該鎖沖突的操作。悲觀并發(fā)控制主要用于數(shù)據(jù)爭(zhēng)用激烈的環(huán)境伴找,以及發(fā)生并發(fā)沖突時(shí)使用鎖保護(hù)數(shù)據(jù)的成本要低于回滾事務(wù)的成本的環(huán)境中盈蛮。

  • 悲觀鎖,正如其名技矮,它指的是對(duì)數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù)抖誉,以及來(lái)自外部系統(tǒng)的事務(wù)處理)修改持悲觀態(tài)度,因此穆役,在整個(gè)數(shù)據(jù)處理過(guò)程中寸五,將數(shù)據(jù)處于鎖定狀態(tài)。
  • 悲觀鎖的實(shí)現(xiàn)耿币,往往依靠數(shù)據(jù)庫(kù)提供的鎖機(jī)制(也只有數(shù)據(jù)庫(kù)層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問(wèn)的排他性梳杏,否則,即使在本系統(tǒng)中實(shí)現(xiàn)了加鎖機(jī)制,也無(wú)法保證外部系統(tǒng)不會(huì)修改數(shù)據(jù))
悲觀鎖的具體流程:
  • 在對(duì)任意記錄進(jìn)行修改前十性,先嘗試為該記錄加上排他鎖(exclusive locking)
  • 如果加鎖失敗叛溢,說(shuō)明該記錄正在被修改,那么當(dāng)前查詢可能要等待或者拋出異常劲适。 具體響應(yīng)方式由開發(fā)者根據(jù)實(shí)際需要決定楷掉。
  • 如果成功加鎖,那么就可以對(duì)記錄做修改霞势,事務(wù)完成后就會(huì)解鎖了烹植。
  • 其間如果有其他對(duì)該記錄做修改或加排他鎖的操作,都會(huì)等待我們解鎖或直接拋出異常愕贡。
在mysql的InnoDB引擎中使用悲觀鎖
  • 其實(shí)就是使用mysql提供的排他鎖草雕。
  • 首先我們得關(guān)閉mysql中的autocommit屬性,因?yàn)閙ysql默認(rèn)使用自動(dòng)提交模式固以,也就是說(shuō)當(dāng)我們進(jìn)行一個(gè)sql操作的時(shí)候墩虹,mysql會(huì)將這個(gè)操作當(dāng)做一個(gè)事務(wù)并且自動(dòng)提交這個(gè)操作。
-- 窗口1:
mysql> start transaction;
Query OK, 0 rows affected

-- 對(duì)7369這行憨琳,加排他鎖
mysql> select * from emp where empno  = 7369 for update;
+-------+-------+-------+------+------------+-----+------+--------+
| EMPNO | ENAME | JOB   | MGR  | HIREDATE   | SAL | COMM | DEPTNO |
+-------+-------+-------+------+------------+-----+------+--------+
|  7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800 | NULL |     20 |
+-------+-------+-------+------+------------+-----+------+--------+
1 row in set
mysql> 

窗口2:
mysql> start transaction;
Query OK, 0 rows affected
-- 執(zhí)行對(duì)該行進(jìn)行修改時(shí)诫钓,一直處于等待狀態(tài)
mysql> update emp set ename =  'aa'  where empno =  7369;
-- 當(dāng)窗口1,提交事務(wù)(commit)后篙螟,窗口2開始執(zhí)行菌湃。
  • 上面的例子展示了排它鎖的原理:一個(gè)鎖在某一時(shí)刻只能被一個(gè)線程占有,其它線程必須等待鎖被釋放之后才可能獲取到鎖或者進(jìn)行數(shù)據(jù)的操作闲擦。
悲觀鎖的優(yōu)點(diǎn)和不足
  • 優(yōu)點(diǎn):悲觀鎖實(shí)際上是采取了先取鎖再訪問(wèn)的策略慢味,為數(shù)據(jù)的處理安全提供了保證场梆。
  • 缺點(diǎn):在效率方面墅冷,由于額外的加鎖機(jī)制產(chǎn)生了額外的開銷,并且增加了死鎖的機(jī)會(huì)或油。并且降低了并發(fā)性寞忿。當(dāng)一個(gè)事物所以一行數(shù)據(jù)的時(shí)候,其他事物必須等待該事務(wù)提交之后顶岸,才能操作這行數(shù)據(jù)腔彰。

樂(lè)觀鎖

在關(guān)系數(shù)據(jù)庫(kù)管理系統(tǒng)里,樂(lè)觀并發(fā)控制(又名“樂(lè)觀鎖”辖佣,Optimistic Concurrency Control霹抛,縮寫“OCC”)是一種并發(fā)控制的方法。它假設(shè)多用戶并發(fā)的事務(wù)在處理時(shí)不會(huì)彼此互相影響卷谈,各事務(wù)能夠在不產(chǎn)生鎖的情況下處理各自影響的那部分?jǐn)?shù)據(jù)杯拐。在提交數(shù)據(jù)更新之前,每個(gè)事務(wù)會(huì)先檢查在該事務(wù)讀取數(shù)據(jù)后,有沒(méi)有其他事務(wù)又修改了該數(shù)據(jù)端逼。如果其他事務(wù)有更新的話朗兵,正在提交的事務(wù)會(huì)進(jìn)行回滾。

  • 樂(lè)觀鎖( Optimistic Locking ) 相對(duì)悲觀鎖而言顶滩,樂(lè)觀鎖假設(shè)認(rèn)為數(shù)據(jù)一般情況下不會(huì)造成沖突余掖,所以在數(shù)據(jù)進(jìn)行提交更新的時(shí)候,才會(huì)正式對(duì)數(shù)據(jù)的沖突與否進(jìn)行檢測(cè)礁鲁,如果發(fā)現(xiàn)沖突了盐欺,則讓返回用戶錯(cuò)誤的信息,讓用戶決定如何去做仅醇。
  • 相對(duì)于悲觀鎖找田,在對(duì)數(shù)據(jù)庫(kù)進(jìn)行處理的時(shí)候,樂(lè)觀鎖并不會(huì)使用數(shù)據(jù)庫(kù)提供的鎖機(jī)制着憨。一般的實(shí)現(xiàn)樂(lè)觀鎖的方式就是記錄數(shù)據(jù)版本墩衙。

數(shù)據(jù)版本,為數(shù)據(jù)增加的一個(gè)版本標(biāo)識(shí)。當(dāng)讀取數(shù)據(jù)時(shí)甲抖,將版本標(biāo)識(shí)的值一同讀出漆改,數(shù)據(jù)每更新一次,同時(shí)對(duì)版本標(biāo)識(shí)進(jìn)行更新准谚。當(dāng)我們提交更新的時(shí)候挫剑,判斷數(shù)據(jù)庫(kù)表對(duì)應(yīng)記錄的當(dāng)前版本信息與第一次取出來(lái)的版本標(biāo)識(shí)進(jìn)行比對(duì),如果數(shù)據(jù)庫(kù)表當(dāng)前版本號(hào)與第一次取出來(lái)的版本標(biāo)識(shí)值相等柱衔,則予以更新樊破,否則認(rèn)為是過(guò)期數(shù)據(jù)。

樂(lè)觀鎖的優(yōu)點(diǎn)和不足:

  • 優(yōu)點(diǎn):樂(lè)觀并發(fā)控制相信事務(wù)之間的數(shù)據(jù)競(jìng)爭(zhēng)(data race)的概率是比較小的唆铐,因此盡可能直接做下去哲戚,直到提交的時(shí)候才去鎖定,所以不會(huì)產(chǎn)生任何鎖和死鎖艾岂。
  • 缺點(diǎn):但如果直接簡(jiǎn)單這么做顺少,還是有可能會(huì)遇到不可預(yù)期的結(jié)果,例如兩個(gè)事務(wù)都讀取了數(shù)據(jù)庫(kù)的某一行王浴,經(jīng)過(guò)修改以后寫回?cái)?shù)據(jù)庫(kù)脆炎,這時(shí)就遇到了問(wèn)題。對(duì)于之前的操作都是無(wú)用功氓辣。

InnoDB鎖的特性

  • 在不通過(guò)索引條件查詢的時(shí)候秒裕,InnoDB使用是表鎖

  • 由于 MySQL 的行鎖是針對(duì)索引加的鎖钞啸,不是針對(duì)記錄加的鎖,所以雖然是訪問(wèn)不同行的記錄几蜻,但是如果是使用相同的索引鍵癞松,是會(huì)出現(xiàn)鎖沖突的。

  • 當(dāng)表有多個(gè)索引的時(shí)候入蛆,不同的事務(wù)可以使用不同的索引鎖定不同的行响蓉,另外,不論 是使用主鍵索引哨毁、唯一索引或普通索引枫甲,InnoDB 都會(huì)使用行鎖來(lái)對(duì)數(shù)據(jù)加鎖。

  • 即便在條件中使用了索引字段扼褪,但是否使用索引來(lái)檢索數(shù)據(jù)是由 MySQL 通過(guò)判斷不同執(zhí)行計(jì)劃的代價(jià)來(lái)決定的想幻,如果 MySQL 認(rèn)為全表掃效率更高,比如:對(duì)一些很小的表话浇,它就不會(huì)使用索引脏毯,這種情況下 InnoDB 將使用表鎖,而不是行鎖幔崖。

  • 因此食店,在分析鎖沖突時(shí),別忘了檢查 SQL 的執(zhí)行計(jì)劃(explain查看)赏寇,以確認(rèn)是否真正使用了索引吉嫩。

  • 有關(guān)執(zhí)行計(jì)劃的解釋可以看著這篇文章:淺談mysql執(zhí)行計(jì)劃之type

1. 通過(guò)非索引項(xiàng)檢索數(shù)據(jù),加表鎖嗅定!
--  price屬性并沒(méi)有加索引自娩,因此這時(shí)候添加的鎖為表級(jí)鎖!
窗口1:
mysql> select * from product where price=88 for update;
+----+------+-------+-----+
| id | name | price | num |
+----+------+-------+-----+
|  2 | 蒙牛 |    88 |   1 |
+----+------+-------+-----+

窗口2:
mysql> update product set price=price-100 where id=6;
這里會(huì)等待渠退,直到窗口1 commit后顯示下面結(jié)果忙迁!
Query OK, 1 row affected
Rows matched: 1  Changed: 1  Warnings: 0
2. 使用相同索引值但是不同行引發(fā)的沖突
-- 這里的num屬性 加上了普通索引,price屬性并沒(méi)有索引
窗口1:
mysql> set autocommit=0;
Query OK, 0 rows affected

mysql> select * from product where num=1 and price=68 for update;
+----+------+-------+-----+
| id | name | price | num |
+----+------+-------+-----+
|  1 | 伊利 |    68 |   1 |
+----+------+-------+-----+

窗口2:
mysql> update product set price=price+100 where num=1 and price=88;
這里會(huì)發(fā)生等待碎乃,直到窗口1 commit 顯示下面結(jié)果
Query OK, 1 row affected
Rows matched: 1  Changed: 1  Warnings: 0
mysql> select * from product;
+----+----------+-------+-----+
| id | name     | price | num |
+----+----------+-------+-----+
|  1 | 伊利     |    68 |   1 |
|  2 | 蒙牛     |   188 |   1 |
+----+----------+-------+-----+
3. 當(dāng)使用索引檢索數(shù)據(jù)時(shí)不同事務(wù)可以操作不同行數(shù)據(jù)
鎖一行數(shù)據(jù)姊扔,DML操作其他行并沒(méi)有影響
窗口1:
mysql> select * from user where id=1 for update;
+----+-------+
| id | price |
+----+-------+
|  1 |   400 |
+----+-------+

窗口2:
mysql> update user set price=price+100 where id=2;
無(wú)需等待窗口1 commit
Database changed
Rows matched: 1  Changed: 1  Warnings: 0

Record Lock、Gap Lock荠锭、Next-key Lock鎖

Record Lock

  • 單條索引上加鎖旱眯,record lock 永遠(yuǎn)鎖的是索引晨川,而非數(shù)據(jù)本身证九,如果innodb表中沒(méi)有索引,那么會(huì)自動(dòng)創(chuàng)建一個(gè)隱藏的聚集索引共虑,鎖住的就是這個(gè)聚集索引愧怜。
  • 所以說(shuō)當(dāng)一條sql沒(méi)有走任何索引時(shí),那么將會(huì)在每一條聚集索引后面加X(jué)鎖妈拌,這個(gè)類似于表鎖拥坛,但原理上和表鎖應(yīng)該是完全不同的蓬蝶。

Gap Lock 間隙鎖

1、什么叫間隙鎖著摔?
mysql> select * from product_copy;
+----+--------+-------+-----+
| id | name   | price | num |
+----+--------+-------+-----+
|  1 | 伊利   |    68 |   1 |
|  2 | 蒙牛   |    88 |   1 |
|  6 | tom    |  2788 |   3 |
| 10 | 優(yōu)衣庫(kù) |   488 |   4 |
+----+--------+-------+-----+
-- 其中id為主鍵 num為普通索引
窗口A:
mysql> select * from product_copy where num=3 for update;
+----+------+-------+-----+
| id | name | price | num |
+----+------+-------+-----+
|  6 | tom  |  2788 |   3 |
+----+------+-------+-----+
1 row in set

窗口B:
mysql> insert into product_copy values(5,'kris',1888,2);
-- 這里會(huì)等待  直到窗口A commit才會(huì)顯示下面結(jié)果
Query OK, 1 row affected

-- 但是下面是不需要等待的
mysql> update product_copy set price=price+100 where num=1;
Query OK, 2 rows affected
Rows matched: 2  Changed: 2  Warnings: 0
mysql> insert into product_copy values(5,'kris',1888,5);
Query OK, 1 row affected
  • 通過(guò)上面的例子可以看出Gap鎖的作用是在1,3的間隙之間加上了鎖缓窜。而且并不是鎖住了表,我更新num=1,5的數(shù)據(jù)是可以的谍咆『檀福可以看出鎖住的范圍是:(1,3]U[3,4)
2摹察、為什么說(shuō) Gap鎖 是RR(repeatable-read/可重復(fù)讀)隔離級(jí)別下防止幻讀的主要原因恩掷?
  1. 解決幻讀的方式很簡(jiǎn)單,就是需要當(dāng)事務(wù)進(jìn)行當(dāng)前讀的時(shí)候供嚎,保證其他事務(wù)不可以在滿足當(dāng)前讀條件的范圍內(nèi)進(jìn)行數(shù)據(jù)操作黄娘。
  2. 根據(jù)索引的有序性,我們可以從上面的例子推斷出滿足where條件的數(shù)據(jù)克滴,只能插入在num=(1,3]U[3,4)兩個(gè)區(qū)間里面寸宏,只要我們將這兩個(gè)區(qū)間鎖住,那么就不會(huì)發(fā)生幻讀偿曙。
3氮凝、主鍵索引/唯一索引 + 當(dāng)前讀 會(huì)加上Gap鎖嗎?
窗口A:
mysql> select * from product_copy where id=6 for update;
+----+------+-------+-----+
| id | name | price | num |
+----+------+-------+-----+
|  6 | tom  |  2788 |   3 |
+----+------+-------+-----+

窗口B:并不會(huì)發(fā)生等待
mysql> insert into product_copy values(5,'kris',1888,3);
Query OK, 1 row affected
  • 例子說(shuō)明的其實(shí)就是行鎖的原因望忆,我只將id=6的行數(shù)據(jù)鎖住了罩阵,用Gap鎖的原理來(lái)解釋的話:因?yàn)橹麈I索引和唯一索引的值只有一個(gè),所以滿足檢索條件的只有一行启摄,故并不會(huì)出現(xiàn)幻讀稿壁,所以并不會(huì)加上Gap鎖
4歉备、通過(guò)范圍查詢是否會(huì)加上Gap鎖傅是?
窗口A:
mysql> select * from product_copy where num>3 for update;
+----+--------+-------+-----+
| id | name   | price | num |
+----+--------+-------+-----+
| 10 | 優(yōu)衣庫(kù) |   488 |   4 |
+----+--------+-------+-----+

窗口B:會(huì)等待
mysql> insert into product_copy values(11,'kris',1888,5);
Query OK, 1 row affected
不會(huì)等待
mysql> insert into product_copy values(3,'kris',1888,2);
Query OK, 1 row affected
  • 其實(shí)原因都是一樣,只要滿足檢索條件的都會(huì)加上Gap鎖
5蕾羊、檢索條件并不存在的當(dāng)前讀會(huì)加上Gap嗎喧笔?
  1. 等值查詢
窗口A:
mysql> select * from product_copy where num=5 for update;
Empty set

窗口B:6 和 4都會(huì)等待
mysql> insert into product_copy values(11,'kris',1888,6);
Query OK, 1 row affected

mysql> insert into product_copy values(11,'kris',1888,4);
Query OK, 1 row affected
  • 原因一樣會(huì)鎖住(4,5]U[5,n)的區(qū)間
  1. 范圍查詢
窗口A:
mysql> select * from product_copy where num>6 for update;
Empty set
窗口B:8 和 4 都會(huì)鎖住
mysql> insert into product_copy values(11,'kris',1888,4);
Query OK, 1 row affected

mysql> insert into product_copy values(11,'kris',1888,8);
Query OK, 1 row affected
  • 上面的2例子看出當(dāng)你查詢并不存在的數(shù)據(jù)的時(shí)候龟再,mysql會(huì)將有可能出現(xiàn)區(qū)間全部鎖住书闸。

Next-Key Lock

  • 這個(gè)鎖機(jī)制其實(shí)就是前面兩個(gè)鎖相結(jié)合的機(jī)制,既鎖住記錄本身還鎖住索引之間的間隙利凑。

死鎖的原理及分析

MVCC

  • MySQL的InnoDB存儲(chǔ)引擎浆劲,實(shí)現(xiàn)的是基于多版本并發(fā)控制協(xié)議—MVCC(Multi-Version Concurrency Control)
  • MVCC最大的好處嫌术,相信也是耳熟能詳:讀不加鎖讀寫不沖突牌借。在讀多寫少的OLTP應(yīng)用中度气,讀寫不沖突是非常重要的,極大的增加了系統(tǒng)的并發(fā)性能膨报,這也是為什么現(xiàn)階段蚯嫌,幾乎所有的RDBMS(關(guān)系數(shù)據(jù)庫(kù)管理系統(tǒng)),都支持了MVCC丙躏。

2PL:Two-Phase Locking

  • 傳統(tǒng)RDBMS(關(guān)系數(shù)據(jù)庫(kù)管理系統(tǒng))加鎖的一個(gè)原則择示,就是2PL (二階段鎖):Two-Phase Locking。
  • 相對(duì)而言晒旅,2PL比較容易理解栅盲,說(shuō)的是鎖操作分為兩個(gè)階段:加鎖階段解鎖階段,并且保證加鎖階段與解鎖階段不相交废恋。下面谈秫,仍舊以MySQL為例,來(lái)簡(jiǎn)單看看2PL在MySQL中的實(shí)現(xiàn)鱼鼓。
image.png

為什么會(huì)發(fā)生死鎖拟烫?

  • MyISAM中是不會(huì)產(chǎn)生死鎖的,因?yàn)镸yISAM總是一次性獲得所需的全部鎖迄本,要么全部滿足硕淑,要么全部等待。
  • 而在InnoDB中嘉赎,鎖是逐步獲得的置媳,就造成了死鎖的可能。(不過(guò)現(xiàn)在一般都是InnoDB引擎公条,關(guān)于MyISAM不做考慮)拇囊。
  • 在InnoDB中,行級(jí)鎖并不是直接鎖記錄靶橱,而是鎖索引寥袭。索引分為主鍵索引和非主鍵索引兩種,如果一條sql語(yǔ)句操作了主鍵索引关霸,MySQL就會(huì)鎖定這條主鍵索引传黄。
  • 如果一條語(yǔ)句操作了非主鍵索引,MySQL會(huì)先鎖定該非主鍵索引,再鎖定相關(guān)的主鍵索引聋呢。
  • 當(dāng)兩個(gè)事務(wù)同時(shí)執(zhí)行,一個(gè)鎖住了主鍵索引淑玫,在等待其他相關(guān)索引。另一個(gè)鎖定了非主鍵索引卧蜓,在等待主鍵索引。這樣就會(huì)發(fā)生死鎖。

通過(guò)兩個(gè)SQL死鎖的例子來(lái)說(shuō)明

1. 兩個(gè)session的兩條語(yǔ)句
image.png
  • 這種情況很好理解惭聂,首先session1獲得 id=1的鎖 session2獲得id=5的鎖,然后session想要獲取id=5的鎖 等待相恃,session2想要獲取id=1的鎖 辜纲,也等待!
2.兩個(gè)session的一條語(yǔ)句
image.png
  • 這種情況需要我們了解數(shù)據(jù)的索引的檢索順序原理簡(jiǎn)單說(shuō)下:普通索引上面保存了主鍵索引拦耐,當(dāng)我們使用普通索引檢索數(shù)據(jù)時(shí)耕腾,如果所需的信息不夠,那么會(huì)繼續(xù)遍歷主鍵索引杀糯。
  • 假設(shè)默認(rèn)情況是RR隔離級(jí)別扫俺,針對(duì)session 1 從name索引出發(fā),檢索到的是(hdc,1)(hdc,6)不僅會(huì)加name索引上的記錄X鎖固翰,而且會(huì)加聚簇索引上的記錄X鎖狼纬,加鎖順序?yàn)橄萚1,hdc,100],后[6,hdc,10] 這個(gè)順序是因?yàn)锽+樹結(jié)構(gòu)的有序性骂际。
  • 而Session 2疗琉,從pubtime索引出發(fā),[10,6],[100,1]均滿足過(guò)濾條件歉铝,同樣也會(huì)加聚簇索引上的記錄X鎖盈简,加鎖順序?yàn)閇6,hdc,10],后[1,hdc,100]太示。
  • 發(fā)現(xiàn)沒(méi)有送火,跟Session 1的加鎖順序正好相反,如果兩個(gè)Session恰好都持有了第一把鎖先匪,請(qǐng)求加第二把鎖种吸,死鎖就發(fā)生了。

避免死鎖呀非,這里只介紹常見的三種

  1. 如果不同程序會(huì)并發(fā)存取多個(gè)表坚俗,盡量約定以相同的順序訪問(wèn)表,可以大大降低死鎖機(jī)會(huì)岸裙。
  2. 在同一個(gè)事務(wù)中猖败,盡可能做到一次鎖定所需要的所有資源,減少死鎖產(chǎn)生概率降允;
  3. 對(duì)于非常容易產(chǎn)生死鎖的業(yè)務(wù)部分恩闻,可以嘗試使用升級(jí)鎖定顆粒度,通過(guò)表級(jí)鎖定來(lái)減少死鎖產(chǎn)生的概率剧董;

轉(zhuǎn):MySQL的鎖機(jī)制和加鎖原理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末幢尚,一起剝皮案震驚了整個(gè)濱河市破停,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌尉剩,老刑警劉巖真慢,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異理茎,居然都是意外死亡黑界,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門皂林,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)朗鸠,“玉大人,你說(shuō)我怎么就攤上這事础倍≈蛘迹” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵著隆,是天一觀的道長(zhǎng)扰楼。 經(jīng)常有香客問(wèn)我,道長(zhǎng)美浦,這世上最難降的妖魔是什么弦赖? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮浦辨,結(jié)果婚禮上蹬竖,老公的妹妹穿的比我還像新娘。我一直安慰自己流酬,他們只是感情好币厕,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著芽腾,像睡著了一般旦装。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上摊滔,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天阴绢,我揣著相機(jī)與錄音,去河邊找鬼艰躺。 笑死呻袭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的腺兴。 我是一名探鬼主播左电,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了篓足?” 一聲冷哼從身側(cè)響起段誊,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎纷纫,沒(méi)想到半個(gè)月后枕扫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體陪腌,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辱魁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了诗鸭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片染簇。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖强岸,靈堂內(nèi)的尸體忽然破棺而出锻弓,到底是詐尸還是另有隱情,我是刑警寧澤蝌箍,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布青灼,位于F島的核電站,受9級(jí)特大地震影響妓盲,放射性物質(zhì)發(fā)生泄漏杂拨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一悯衬、第九天 我趴在偏房一處隱蔽的房頂上張望弹沽。 院中可真熱鬧,春花似錦筋粗、人聲如沸策橘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)丽已。三九已至,卻和暖如春买决,著一層夾襖步出監(jiān)牢的瞬間沛婴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工策州, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瘸味,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓够挂,卻偏偏與公主長(zhǎng)得像旁仿,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容