MileStone
上一篇,我們講了Mysql的樂觀鎖和悲觀鎖,大家有興趣可以連起來一起看。
本文需要閱讀時(shí)間大約在1小時(shí),請(qǐng)抽出完整的時(shí)間來閱讀,拒絕一目十行。
后面會(huì)按照下圖,分批次對(duì)Mysql的鎖和大家一起分享
??花絮
??仔細(xì)看哦 ??
今天采蜜采到一份看起來不錯(cuò)的簡歷,正襟危坐,電話call過去。
對(duì)方的電話響著粵語版的《明年今日》,讓我想起了村里的小芳,鞭子粗又長。
“喂,哪位?”,對(duì)方的聲音很Young很有錢。
“同學(xué)你好,我是巴巴的面試官,你對(duì)XX巴巴有興趣嗎,現(xiàn)在方不方便聊聊?”。
“...你好,有啊,方便的。”
“okay,那我們簡單的聊一會(huì),你能先簡單的介紹一下自己嗎?”。
“好,我叫小明,在OO上班,工作3年,無房無車,有個(gè)女朋友叫清風(fēng)...”
吧啦吧啦......
“項(xiàng)目用到了SpringBoot、MQ、Mysql...”
“好的,那我們聊一聊mysql的鎖吧!”。
1. Mysql的事務(wù)
偉人說:“想了解一件事情,你就得了解它的前因后果”,
想了解透徹mysql的鎖,不得不說mysql的事務(wù)機(jī)制。
于是我問小明:你對(duì)mysql的事務(wù)了解嗎?
小明輕咳一下,娓娓道來:
1.1 事務(wù)概述
數(shù)據(jù)庫事務(wù)是數(shù)據(jù)庫執(zhí)行過程中的一個(gè)邏輯單位,一個(gè)事務(wù)通常包含了對(duì)數(shù)據(jù)庫的讀/寫操作。它的存在包含有以下兩個(gè)目的:
- 為數(shù)據(jù)庫操作序列提供了一個(gè)回滾的方法,同時(shí)提供了數(shù)據(jù)庫即使在異常狀態(tài)下仍能保持一致性的方法。
- 當(dāng)多個(gè)應(yīng)用程序在并發(fā)訪問數(shù)據(jù)庫時(shí),可以在這些應(yīng)用程序之間提供一個(gè)隔離方法(版面問題下次討論),以防止彼此的操作互相干擾。
1.2 事務(wù)的特性:ACID
- 原子性 aotmic
事務(wù)必須是原子工作單元;對(duì)于其數(shù)據(jù)修改,要么全都執(zhí)行,要么全都不執(zhí)行。
- 一致性 consistent
事務(wù)在完成時(shí),必須使所有的數(shù)據(jù)都保持一致狀態(tài)。
- 隔離性 isolaton
由并發(fā)事務(wù)所作的修改必須與任何其它并發(fā)事務(wù)所作的修改隔離。事務(wù)查看數(shù)據(jù)時(shí)數(shù)據(jù)所處的狀 態(tài),要么是另一并發(fā)事務(wù)修改它之前的狀態(tài),要么是另一事務(wù)修改它之后的狀態(tài),事務(wù)不會(huì)查看中間狀態(tài)的數(shù)據(jù)
- 持久性 duration
事務(wù)完成后,它對(duì)系統(tǒng)的影響是永久性的
2. 排他鎖&共享鎖
我心想,這個(gè)誰都懂,繼續(xù)追問:那你能在講講鎖嗎?
好的,那我講一下Mysql的幾個(gè)鎖。小明竊喜,我已經(jīng)看了imooc(公眾號(hào)微信)大神的鎖了,我怕你?
2.1 共享鎖(shared locks,S鎖)
小明說:在你沒有女朋友的時(shí)候,你想和女人滾床單時(shí)候,只能去找紅燈區(qū),別人也可以找紅燈區(qū),這就是共享!
共享鎖又叫讀鎖,如果事務(wù)T1對(duì)行R加上S鎖,則
- 其它事務(wù)T2/T3/Tn只能對(duì)行R再加S鎖,不能加其它鎖
- 獲得S鎖的事務(wù)只能讀數(shù)據(jù),不能寫數(shù)據(jù)(你傻呀,當(dāng)然也不能刪咯)。
select … lock in share mode;
2.2 排他鎖(exclusive locks,X鎖)
小明說:你有錢找了個(gè)女票,你就要獨(dú)占這個(gè)女票,別人不能使用,看(讀)都不行!這就是排他!我們重點(diǎn)說一下排它鎖。
排它鎖又叫寫鎖,如果事務(wù)T1對(duì)行R加上X鎖,則
- 其它事務(wù)T2/T3/Tn都不能對(duì)行R加任何類型的鎖,直到T1事務(wù)在行R上的X鎖釋放。
- 獲得X鎖的事務(wù)既能讀數(shù)據(jù),又能寫數(shù)據(jù)(也可以刪除數(shù)據(jù))。
select ... for update
舉例表USER:
id | name | desc |
---|---|---|
1 | 馬云 | 首富 |
2 | 小明 | 首負(fù) |
T1(事務(wù)1):
// start T1
SELECT * FROM USER WHERE id = 1 lock in share mode; (S鎖)
......
// start T2
UPDATE USER SET name = '小明' WHERE id = 1;
......
如果T1不進(jìn)行提交,則S鎖不會(huì)釋放,
那么T2就拿著X鎖眼巴巴的看著,一直等待T1(事務(wù)1)釋放S鎖。
此時(shí)
// 接上文代碼塊
// start T3
// 此時(shí),如果 T3 做同樣查詢,可以直接獲取S鎖進(jìn)行查詢
SELECT * FROM USER WHERE id = 1 lock in share mode; (S鎖)
這個(gè)時(shí)候如果T1(事務(wù)1)要進(jìn)行 DELETE 操作
// start T1
SELECT * FROM USER WHERE id = 1 lock in share mode; (S鎖)
......
DELETE FROM USER WHERE id = 1;
......
此時(shí):
T1發(fā)現(xiàn)X鎖被T2占據(jù)著,所以T1拿不到X鎖一直等待T2釋放X鎖,而T2拿著X鎖等待T1釋放S鎖,這樣互相等待就產(chǎn)生了死鎖,deadLock。
發(fā)生死鎖以后,InnoDB 會(huì)產(chǎn)生錯(cuò)誤信息,并且釋放鎖(后面會(huì)專門講業(yè)務(wù)中遇到的死鎖和解決方案)。
上述X鎖的代碼可以用下圖來顯示:
用時(shí)間流程來顯示3個(gè)線程的交互如下:
你可以把排它鎖對(duì)行的保護(hù),看作你對(duì)你女的保護(hù),只能你碰,別人不能動(dòng)!前提你得有女,不認(rèn)真學(xué)習(xí),你就是個(gè)屌絲...單身狗
聽到這里我覺得小明同學(xué)很有意思哦,這個(gè)栗子??舉得很有力氣很有代入感!不愧是有女朋友的人啊!
3. 意向鎖
我對(duì)小明已經(jīng)開始有點(diǎn)興趣了,繼續(xù)追問,那你能否再說一下意向鎖?
好!
3.1 意向鎖(Intention Locks)
小明說: 他在找蒼老師或者女票時(shí)候,先要遠(yuǎn)程看看對(duì)方有沒有被人家共享或者獨(dú)占,而不是到面前有沒有人鎖住她,可以節(jié)省成本!
意向鎖是表鎖,多用在innoDB中,是數(shù)據(jù)庫自身的行為,不需要人工干預(yù),在事務(wù)結(jié)束后會(huì)自行解除。
意向鎖分為意向共享鎖(IS鎖)和意向排它鎖(IX鎖)
- 鎖:表示事務(wù)中將要對(duì)某些行加S鎖
- IX鎖:表示事務(wù)中將要對(duì)某些行加X鎖
意向鎖的主要作用是提升存儲(chǔ)引擎性能,innoDB中的S鎖和X鎖是行鎖,每當(dāng)事務(wù)到來時(shí),存儲(chǔ)引擎需要遍歷所有行的鎖持有情況,性能較低,因此引入意向鎖,檢查行鎖前先檢查意向鎖是否存在,如果存在則阻塞線程。
3.2 意向鎖的使用
順上面的思路講下去,我們看下使用的邏輯
舉個(gè)栗子??:
T1:
SELECT * FROM A WHERE id = 1 lock in share mode;(加S鎖)
T2:
SELECT * FROM A WHERE id > 0 for update; (加X鎖)
看上面這2個(gè)SQL事務(wù),T1執(zhí)行時(shí)候,對(duì)id=1這行加上了S鎖,T2執(zhí)行前,需要獲取全表的更新鎖進(jìn)行判斷,即:
step1:判斷表A是否有表級(jí)鎖
step2:判斷表A每一行是否有行級(jí)鎖
當(dāng)數(shù)據(jù)量較大時(shí)候(我們一張表一般500-5000萬數(shù)據(jù)),step2這種判斷極其低效。
亞麻跌!亞麻跌!亞麻跌!于是乎,我們就need意向鎖協(xié)議。
意向鎖協(xié)議
- 事務(wù)要獲取表A某些行的S鎖必須要獲取表A的IS鎖
- 事務(wù)要獲取表A某些行的X鎖必須要獲取表A的IX鎖
Now!do you get me sense ?
這個(gè)時(shí)候step2就改變成了對(duì)意向鎖的判斷
step2:發(fā)現(xiàn)表A有IS鎖,說明表肯定有行級(jí)的S鎖,因此,T2申請(qǐng)X鎖阻塞等待,不需要判斷全表,判斷效率極大提高(是不是省了很多錢)
4. 間隙鎖
小明先飚了一段英語,想來壓制我?
但是我并不care,who care?9012年了,誰不會(huì)英語?so easy!我可是從小用步步高點(diǎn)讀機(jī)長大的人哦。
我say:pardon ? (上小學(xué)我就會(huì),小樣,你再飚啊!)
4.0 官方原文:
非常重要,必須讀懂,讀不懂,就去幼兒園回爐吧,親
A gap lock is a lock on a gap between index records, or a lock on the gap before the first or after the last index record. For example, SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE; prevents other transactions from inserting a value of 15 into column t.c1, whether or not there was already any such value in the column, because the gaps between all existing values in the range are locked.
當(dāng)我們用范圍條件條件檢索數(shù)據(jù)(非聚簇索引、非唯一索引),并請(qǐng)求共享或排他鎖時(shí),InnoDB會(huì)給符合條件的數(shù)據(jù)記錄的索引項(xiàng)加鎖;對(duì)于鍵值在條件范圍內(nèi)但并不存在的記錄,稱為間隙,InnoDB也會(huì)為這些間隙加鎖,即間隙鎖。
Next-Key鎖是符合條件的行鎖加上間隙鎖
4.1 間隙鎖產(chǎn)生的條件
在InnoDB下,間隙鎖的產(chǎn)生需要滿足三個(gè)條件:
- 隔離級(jí)別為RR
- 當(dāng)前讀
- 查詢條件能夠走到索引
4.2 間隙鎖的作用
MySQL官方文檔:間隙鎖的目的是為了讓其他事務(wù)無法在間隙中新增數(shù)據(jù)。
在RR模式的InnoDB中,間隙鎖能起到兩個(gè)作用:
1. 保障數(shù)據(jù)的恢復(fù)和復(fù)制
2. 防止幻讀
- 防止在間隙中執(zhí)行insert語句
- 防止將已有數(shù)據(jù)update到間隙中
數(shù)據(jù)庫數(shù)據(jù)的恢復(fù)和復(fù)制是通過binlog實(shí)現(xiàn)的,binlog中記錄了執(zhí)行成功的DML語句(在阿里得到了極廣泛的應(yīng)用),在數(shù)據(jù)恢復(fù)時(shí)需要保證數(shù)據(jù)之間的事務(wù)順序,間隙鎖可以避免在一批數(shù)據(jù)中插入其他事務(wù)。
對(duì)于間隙鎖,筆者人為,你只要能get到上述英語片段的點(diǎn),就Okay了,小學(xué)5年級(jí)就能讀懂,如果讀不懂,你真的要去學(xué)一下英語了,不會(huì)英語的程序員,一定是個(gè)最low逼的程序員!
廣而告之
最后,做個(gè)廣告,現(xiàn)在阿里開放為數(shù)不多的HC,如果有想來的同學(xué),給我私聊,幫你內(nèi)推哦~簡歷飛來吧,同學(xué)們。
和小明的故事還在繼續(xù),他又回去看imooc的公眾號(hào)去了,很快就會(huì)回來。