mysql 鎖機(jī)制
標(biāo)簽(空格分隔): mysql
參考文檔
- https://www.2cto.com/database/201508/429967.html
- http://www.cnblogs.com/aipiaoborensheng/p/5767459.html
概念
- 共享鎖(S):允許一個(gè)事務(wù)去讀一行,阻止其他事務(wù)獲得相同的數(shù)據(jù)集的排他鎖。
- 排他鎖(X):允許獲得排他鎖的事務(wù)更新數(shù)據(jù),但是組織其他事務(wù)獲得相同數(shù)據(jù)集的共享鎖和排他鎖。
- 對(duì)于insert、update、delete,InnoDB會(huì)自動(dòng)給涉及的數(shù)據(jù)加排他鎖(X);對(duì)于一般的Select語(yǔ)句,InnoDB不會(huì)加任何鎖,事務(wù)可以通過(guò)以下語(yǔ)句給顯示加共享鎖或排他鎖。
共享鎖
select * from table_name where .....lock in share mode
Note left of 事務(wù)1: select * from table_1 where id=1 lock in share mode;
事務(wù)1-->事務(wù)2:
Note right of 事務(wù)2: select * from table_1 where id=1 lock in share mode;
事務(wù)2-->事務(wù)1:
Note left of 事務(wù)1: update table_1 set age=10 where id=1;
Note left of 事務(wù)1: 事務(wù)1更新時(shí)發(fā)現(xiàn)此行鎖被其他事務(wù)享用,等待
事務(wù)1-->事務(wù)2:
Note right of 事務(wù)2: update table_1 set age=12 where id=1;
Note right of 事務(wù)2: 事務(wù)2更新時(shí)發(fā)現(xiàn)此行鎖被其他事務(wù)享用,也等待,導(dǎo)致死鎖
排他鎖
select * from table_name where .....for update
Note left of 事務(wù)1: select * from table_1 where id=1 for update;
事務(wù)1-->事務(wù)2:
Note right of 事務(wù)2: select * from table_1 where id=1 for update;
Note right of 事務(wù)2: 等待...
事務(wù)2-->事務(wù)1:
Note left of 事務(wù)1: update table_1 set age=10 where id=1;
Note left of 事務(wù)1: 更新完后釋放鎖
事務(wù)1-->事務(wù)2:
Note right of 事務(wù)2: 獲得鎖后,得到其他事務(wù)提交的記錄
行鎖的三種形式
- Record lock:鎖定一條記錄。
- Gap lock
- Next-key lock
innoDB鎖問(wèn)題
事務(wù)(Transaction)及其ACID屬性
- 原子性(Actomicity):事務(wù)是一個(gè)原子操作單元,其對(duì)數(shù)據(jù)的修改,要么全都執(zhí)行,要么全都不執(zhí)行。
- 一致性(Consistent):在事務(wù)開(kāi)始和完成時(shí),數(shù)據(jù)都必須保持一致?tīng)顟B(tài)。這意味著所有相關(guān)的數(shù)據(jù)規(guī)則都必須應(yīng)用于事務(wù)的修改,以操持完整性;事務(wù)結(jié)束時(shí),所有的內(nèi)部數(shù)據(jù)結(jié)構(gòu)(如B樹(shù)索引或雙向鏈表)也都必須是正確的。
- 隔離性(Isolation):數(shù)據(jù)庫(kù)系統(tǒng)提供一定的隔離機(jī)制,保證事務(wù)在不受外部并發(fā)操作影響的“獨(dú)立”環(huán)境執(zhí)行。這意味著事務(wù)處理過(guò)程中的中間狀態(tài)對(duì)外部是不可見(jiàn)的,反之亦然。
- 持久性(Durable):事務(wù)完成之后,它對(duì)于數(shù)據(jù)的修改是永久性的,即使出現(xiàn)系統(tǒng)故障也能夠保持。
并發(fā)事務(wù)帶來(lái)的問(wèn)題
- 更新丟失(Lost Update):當(dāng)兩個(gè)或多個(gè)事務(wù)選擇同一行,然后基于最初選定的值更新該行時(shí),由于每個(gè)事務(wù)都不知道其他事務(wù)的存在,就會(huì)發(fā)生丟失更新問(wèn)題——最后的更新覆蓋了其他事務(wù)所做的更新。例如,兩個(gè)編輯人員制作了同一文檔的電子副本。每個(gè)編輯人員獨(dú)立地更改其副本,然后保存更改后的副本,這樣就覆蓋了原始文檔。最后保存其更改保存其更改副本的編輯人員覆蓋另一個(gè)編輯人員所做的修改。如果在一個(gè)編輯人員完成并提交事務(wù)之前,另一個(gè)編輯人員不能訪問(wèn)同一文件,則可避免此問(wèn)題
- 臟讀(Dirty Reads):一個(gè)事務(wù)正在對(duì)一條記錄做修改,在這個(gè)事務(wù)并提交前,這條記錄的數(shù)據(jù)就處于不一致?tīng)顟B(tài);這時(shí),另一個(gè)事務(wù)也來(lái)讀取同一條記錄,如果不加控制,第二個(gè)事務(wù)讀取了這些“臟”的數(shù)據(jù),并據(jù)此做進(jìn)一步的處理,就會(huì)產(chǎn)生未提交的數(shù)據(jù)依賴關(guān)系。這種現(xiàn)象被形象地叫做“臟讀”。
- 不可重復(fù)讀(Non-Repeatable Reads):一個(gè)事務(wù)在讀取某些數(shù)據(jù)已經(jīng)發(fā)生了改變、或某些記錄已經(jīng)被刪除了!這種現(xiàn)象叫做“不可重復(fù)讀”。
- 幻讀(Phantom Reads):一個(gè)事務(wù)按相同的查詢條件重新讀取以前檢索過(guò)的數(shù)據(jù),卻發(fā)現(xiàn)其他事務(wù)插入了滿足其查詢條件的新數(shù)據(jù),這種現(xiàn)象就稱為“幻讀”。
事務(wù)隔離級(jí)別
| 隔離級(jí)別 | 臟讀| 不可重復(fù)讀 | 幻讀 |
| -| - |
| 未提交讀(Read uncommitted) | √ | √ | √ |
| 已提交度(Read committed) | x | √ | √ |
| 可重復(fù)讀(Repeatable read) |x | x | √ |
| 可序列化(Serializable) | x | x | x |
mysql行鎖的特性
innodb 的行鎖是在有索引的情況下,沒(méi)有索引的表是鎖定全表的.
實(shí)例:
id是主鍵
| id| name|
| -| - |
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
事務(wù)1update第一條id=1的數(shù)據(jù),事務(wù)不提交;事務(wù)2接著update第二條id=2的數(shù)據(jù)的時(shí)候等待,原因是id沒(méi)有加上索引,導(dǎo)致事務(wù)1鎖的是表鎖而不是行鎖。如果是使用相同的索引鍵,會(huì)出現(xiàn)鎖沖突。
示例:tab_with_index表中id字段有索引,name字段沒(méi)有索引。
事務(wù)1:
select * from tab_with_index where id = 1 and name = '1' for update;
事務(wù)2:
select * from tab_with_index where id = 1 and name = '4' for update;
雖然事務(wù)2訪問(wèn)的是和事務(wù)1不同的記錄,但是因?yàn)槭褂昧讼嗤乃饕孕枰却i。
- 當(dāng)表有多個(gè)索引的時(shí)候,不同的事務(wù)可以使用不同的索引鎖定不同的行,另外,不論是使用主鍵索引、唯一索引或普通索引,InnoDB都會(huì)使用行鎖來(lái)對(duì)數(shù)據(jù)加鎖。
示例:表tab_with_index的id字段有主鍵索引,name字段有普通索引。
事務(wù)1:
select * from tab_with_index where id = 1 for update;
事務(wù)2:
select * from tab_with_index where name = '2' for update;
事務(wù)2使用name的索引訪問(wèn)記錄,因?yàn)橛涗洓](méi)有被索引,所以也可以獲得鎖。
間隙鎖(Next-Key鎖)
當(dāng) 我們用范圍條件而不是相等條件檢索數(shù)據(jù),并請(qǐng)求共享或排他鎖時(shí),InnoDB會(huì)給符合條件 的已有數(shù)據(jù)記錄的索引項(xiàng)加鎖;對(duì)于鍵值在條件范圍內(nèi)但并不存在的記錄,叫做“間隙(GAP)”,InnoDB也會(huì)對(duì)這個(gè)“間隙”加鎖,這種鎖機(jī)制就是所謂 的間隙鎖(Next-Key鎖)。
示例:
Select * from emp where empid > 100 for update;
是一個(gè)范圍條件的檢索,InnoDB不僅會(huì)對(duì)符合條件的empid值為101的記錄加鎖,也會(huì)對(duì)empid大于101(這些記錄并不存在)的“間隙”加鎖。
InnoDB 使用間隙鎖的目的,一方面是為了防止幻讀,以滿足相關(guān)隔離級(jí)別的要求,對(duì)于上面的例子,要是不使用間隙鎖,如果其他事務(wù)插入了empid大于100的任何 記錄,那么本事務(wù)如果再次執(zhí)行上述語(yǔ)句,就會(huì)發(fā)生幻讀.
還要特別說(shuō)明的是,InnoDB除了通過(guò)范圍條件加鎖時(shí)使用間隙鎖外,如果使用相等條件請(qǐng)求給一個(gè)不存在的記錄加鎖,InnoDB也會(huì)使用間隙鎖!
一致性非鎖定讀
一致性非鎖定讀是指InnoDB存儲(chǔ)引擎通過(guò)多版本并發(fā)控制技術(shù)來(lái)讀取當(dāng)前數(shù)據(jù)庫(kù)的數(shù)據(jù)。如果當(dāng)前讀取的行正在執(zhí)行delete或者update操作,這時(shí)讀取操作不會(huì)等行鎖的釋放,而是去讀取行的快照數(shù)據(jù)。
快照數(shù)據(jù)是指改行之前版本的數(shù)據(jù),該實(shí)現(xiàn)是通過(guò)undo段來(lái)實(shí)現(xiàn)的,而undo段用來(lái)在事務(wù)中保存回滾數(shù)據(jù),因此使用快照沒(méi)有增加額外的開(kāi)銷。
這是InnoDB存儲(chǔ)引擎的默認(rèn)讀取方式。
注意
- 不同的事務(wù)隔離級(jí)別下讀取的方式不同,并不是每個(gè)事務(wù)隔離級(jí)別下都是采用非鎖定的一致性讀。四種隔離級(jí)別中,READ COMMITTED和REPEATABLE READ這兩種隔離級(jí)別使用非鎖定的一致性讀。
- 不同的事務(wù)即使都使用非鎖定的一致性讀,但是對(duì)于快照數(shù)據(jù)的定義也各不相同。READ COMMITTED級(jí)別下非鎖定讀總是讀取鎖定行的最新一份快照數(shù)據(jù);而REPEATABLE READ級(jí)別下非鎖定讀總是讀取事務(wù)開(kāi)始時(shí)的數(shù)據(jù)版本。
例子
事務(wù)A | 事務(wù)B |
---|---|
select * from table where id='1'; | |
. | update table set id =3 where id=1; |
select * from table where id='1'; | |
. | commit; |
select * from table where id='1'; |
上述例子中事務(wù)B update以后事務(wù)A第一次select的時(shí)候RC級(jí)別和RR級(jí)別獲取的結(jié)果都是id=1的那一條數(shù)據(jù);第二次select的時(shí)候,由于事務(wù)B已經(jīng)提交,RC級(jí)別select的結(jié)果就是id=3,而RR級(jí)別讀取的是事務(wù)開(kāi)始時(shí)的數(shù)據(jù),id=1。
一致性鎖定讀
默認(rèn)配置下事務(wù)的隔離級(jí)別為REPEATABLE READ,select操作為非一致性鎖定讀,但某些情況下需要對(duì)數(shù)據(jù)庫(kù)讀取操作進(jìn)行加鎖保證數(shù)據(jù)的一致性。select 有兩種一致的鎖定讀:
- select ... for update
- select ... lock in share mode
自增長(zhǎng)與鎖
InnoDB存儲(chǔ)引擎內(nèi)部對(duì)每個(gè)含有自增長(zhǎng)列的表有一個(gè)自增長(zhǎng)計(jì)數(shù)器,當(dāng)進(jìn)行insert操作時(shí),首先獲取計(jì)數(shù)器的最大值,加1后進(jìn)行insert操作。這個(gè)操作會(huì)加一個(gè)特殊的表鎖,AUTO-INC LOCK。這個(gè)鎖并不是在事務(wù)提交后才釋放,而是在insert語(yǔ)句執(zhí)行完后釋放。
缺點(diǎn)
雖然是insert后就釋放鎖,不是事務(wù)提交后才釋放,但是必須等前一個(gè)insert的完成才能進(jìn)行下一次insert,性能較差。
改進(jìn)
TODO...
外鍵與鎖
在對(duì)外鍵值進(jìn)行update和insert操作時(shí)首先需要查詢父表的記錄,即select父表,這個(gè)select操作不是使用一致性非鎖定讀,因?yàn)闀?huì)發(fā)生數(shù)據(jù)不一致的問(wèn)題,為此需要使用一致性鎖定讀,這時(shí)使用的select ... lock in share mode方式。當(dāng)父表對(duì)應(yīng)記錄加X(jué)鎖后,子表的操作將會(huì)阻塞。
例子
TODO...
鎖的算法
InnoDB存儲(chǔ)引擎有三種行鎖的算法
- Record Lock:單行記錄上鎖
- Gap Lock:間隙鎖,鎖定一個(gè)范圍
- Next-Key Lock