數(shù)據(jù)庫鎖的種類包括:
==行鎖、表鎖觅闽、共享鎖、排它鎖涮俄、樂觀鎖蛉拙、悲觀鎖==
按照鎖粒度劃分,可以將鎖劃分成
- 行鎖??
- 表鎖??
按照數(shù)據(jù)庫管理角度劃分彻亲,可以將鎖分成排他鎖和共享鎖
- 共享鎖??
- 排他鎖??
按程序員角度劃分孕锄,可分為樂觀鎖和悲觀鎖
- 樂觀鎖??
- 悲觀鎖??
==1、行鎖 & 表鎖==
行記錄苞尝,表都是資源畸肆,鎖是作用在這些資源上的。
當(dāng)插入數(shù)據(jù)時宙址,就鎖定表轴脐,這叫做”鎖表”;當(dāng)更新數(shù)據(jù)時,就鎖定行豁辉,這叫做”鎖行”令野。
如果粒度比較小(比如行級鎖),可以 增加系統(tǒng)的并發(fā)量 但需要較大的系統(tǒng)開銷徽级,會影響到性能气破,出現(xiàn) #死鎖#。因?yàn)榱6刃t操作的鎖的數(shù)量會增加餐抢。
如果作用在表上现使,粒度大,開銷小旷痕,維護(hù)的鎖少碳锈,不會出現(xiàn)死鎖,但是并發(fā)是相當(dāng)昂貴的欺抗,因?yàn)殒i定了整個表就限制了其它事務(wù)對這個表中其他記錄的訪問售碳。
InnoDB 和 Oracle 支持行鎖和表鎖,MyISAM 只支持表鎖
mysql的行鎖是基于索引
加載的绞呈,所以行鎖是要加在索引響應(yīng)的行上贸人,即命中索引
如上圖所示,數(shù)據(jù)庫表中有一個主鍵索引和一個普通索引佃声,Sql語句基于索引查詢艺智,命中兩條記錄。
此時行鎖一鎖就鎖定兩條記錄圾亏,當(dāng)其他事務(wù)訪問數(shù)據(jù)庫同一張表時十拣,被鎖定的記錄不能被訪問,其他的記錄都可以訪問到志鹃。
行鎖演示:
行鎖里面又細(xì)分了:記錄鎖夭问、間隙鎖、意向鎖
# 記錄鎖
select * from [tb_name] where id = #{id} for update
id列上有唯一索引曹铃,并且查詢條件可以唯一確定一條記錄甲喝,這時候innodb使用記錄鎖,只會鎖住查出來的這一行記錄
# 間隙鎖
where后面的字段有索引铛只,但不是唯一索引,或者使用了>, < 等范圍的查詢條件時糠溜,查詢條件范圍內(nèi)的索引值之間的間隙會被加鎖淳玩,結(jié)果就是被加鎖的間隙之間不能插入索引值
表鎖響應(yīng)的是非索引字段
,即==全表掃描==非竿,全表掃描時鎖定整張表蜕着,sql語句可以通過執(zhí)行計劃看出掃描了多少條記錄。
==可以看到,當(dāng)更新數(shù)據(jù)庫數(shù)據(jù)時承匣,如果沒有觸發(fā)索引蓖乘,則會鎖表,鎖表后再對表做任何變更操作都會導(dǎo)致鎖沖突韧骗,所以表鎖的鎖沖突概率較高嘉抒。==
# 表鎖分5類:共享鎖(S鎖)、排它鎖(X鎖)袍暴、行級共享鎖(RS鎖)些侍、行級排它鎖(RX鎖)、共享行級排它鎖(SRX鎖)
==2政模、共享鎖 & 排他鎖==
共享鎖岗宣,也叫讀鎖,或者 S 鎖淋样,共享鎖鎖定的資源可以被其他用戶讀取耗式,但不能修改。
在進(jìn)行 SElECT 的時候趁猴,會將對象進(jìn)行共享鎖鎖定刊咳,當(dāng)數(shù)據(jù)讀取完畢之后,就會釋放共享鎖躲叼,這樣就可以保證數(shù)據(jù)在讀取時不被修改芦缰。
給某個表加共享鎖:
lock table goods read;
當(dāng)數(shù)據(jù)表加上共享鎖的時候,該表數(shù)據(jù)就會變成只讀模式枫慷,當(dāng)時我們想更新 goods 表中的數(shù)據(jù)會報錯让蕾,比如:
update goods set good_id = 12345 where user_id = 95678;
系統(tǒng)報錯如下:
ERROR 1099 (HY000): Table 'goods' was locked with a READ lock and can't be updated
對表共享鎖進(jìn)行解鎖:
unlock table; -- 會釋放當(dāng)前會話的所有鎖
給某數(shù)據(jù)行加鎖
select ... where ... lock table tablename in share mode 語法鎖定整個表
排他鎖,也叫做獨(dú)占鎖或听,寫鎖或者 X 鎖探孝,排他鎖鎖定的數(shù)據(jù)只允許進(jìn)行鎖定操作的事務(wù)使用,
其他事務(wù)無法對已鎖定的數(shù)據(jù)進(jìn)行查詢或者修改誉裆。
給某個表加排他鎖:
lock table goods write;
排他鎖的事務(wù)可以對 goods 進(jìn)行查詢和修改顿颅。其他事務(wù)如果想要在 goods 表上查詢數(shù)據(jù),則需要等待足丢。
對排他鎖進(jìn)行解鎖:
unlock table;
給某行數(shù)據(jù)加排他鎖
select ... for update
當(dāng)我們對數(shù)據(jù)進(jìn)行更新的時候會自動使用排他鎖粱腻,也就是 insert ,delete 或者 update 的時候斩跌,數(shù)據(jù)庫自動使用排他鎖绍些,防止其他事務(wù)對改數(shù)據(jù)進(jìn)行操作。
==3耀鸦、樂觀鎖 & 悲觀鎖==
樂觀鎖總是假設(shè)最好的情況柬批,每次去拿數(shù)據(jù)的時候都認(rèn)為別人不會修改啸澡,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數(shù)據(jù)氮帐,可以使用版本號機(jī)制和CAS算法實(shí)現(xiàn)嗅虏。**樂觀鎖適用于多讀的應(yīng)用類型,這樣可以提高吞吐量上沐。
版本號機(jī)制:
一般是在數(shù)據(jù)表中加上一個數(shù)據(jù)版本號version字段皮服,表示數(shù)據(jù)被修改的次數(shù),當(dāng)數(shù)據(jù)被修改時奄容,version值會加一冰更。當(dāng)線程A要更新數(shù)據(jù)值時,在讀取數(shù)據(jù)的同時也會讀取version值昂勒,在提交更新時蜀细,若剛才讀取到的version值為當(dāng)前數(shù)據(jù)庫中的version值相等時才更新,否則重試更新操作戈盈,直到更新成功奠衔。
update ... set version=version+1 where version=version
悲觀鎖總是假設(shè)最壞的情況,每次去拿數(shù)據(jù)的時候都認(rèn)為別人會修改塘娶,所以每次在拿數(shù)據(jù)的時候都會上鎖归斤,這樣別人想拿這個數(shù)據(jù)就會阻塞直到它拿到鎖(共享資源每次只給一個線程使用,其它線程阻塞刁岸,用完后再把資源轉(zhuǎn)讓給其它線程)脏里。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫里邊就用到了很多這種鎖機(jī)制,比如行鎖虹曙,表鎖等迫横,讀鎖,寫鎖等酝碳,都是在做操作之前先上鎖矾踱。
通過數(shù)據(jù)庫自身的鎖機(jī)制來實(shí)現(xiàn),從而保證數(shù)據(jù)操作的排他性
==樂觀鎖適合讀操作多的場景; 悲觀鎖適合寫操作多的場景==
非原創(chuàng)疏哗,搬代碼的小工整理呛讲。。返奉。