概述
相對(duì)其他數(shù)據(jù)庫(kù)而言卧惜,MySQL的鎖機(jī)制比較簡(jiǎn)單介褥,其最顯著的特點(diǎn)是不同的存儲(chǔ)引擎支持不同的鎖機(jī)制。比如,MyISAM和MEMORY存儲(chǔ)引擎采用的是表級(jí)鎖(table-level locking)堂油;InnoDB存儲(chǔ)引擎既支持行級(jí)鎖( row-level locking)赶掖,也支持表級(jí)鎖竿裂,但默認(rèn)情況下是采用行級(jí)鎖许帐。
表級(jí)鎖: 開(kāi)銷小,加鎖快主经;不會(huì)出現(xiàn)死鎖(因?yàn)镸yISAM會(huì)一次性獲得SQL所需的全部鎖)荣暮;鎖定粒度大,發(fā)生鎖沖突的概率最高,并發(fā)度最低罩驻。
行級(jí)鎖: 開(kāi)銷大穗酥,加鎖慢;會(huì)出現(xiàn)死鎖惠遏;鎖定粒度最小砾跃,發(fā)生鎖沖突的概率最低,并發(fā)度也最高。
MyISAM鎖細(xì)述
MySQL的表級(jí)鎖有兩種模式: 表共享讀鎖(Table Read Lock)和表獨(dú)占寫鎖(Table Write Lock)节吮。
當(dāng)MyISAM在執(zhí)行查詢語(yǔ)句時(shí)抽高,會(huì)自動(dòng)給涉及到表加讀鎖,在執(zhí)行更新操作時(shí)透绩,會(huì)加寫鎖翘骂。當(dāng)然用戶也可以用LOCK TABLE 去顯式的加鎖。顯式的加鎖一般是應(yīng)用于:需要在一個(gè)時(shí)間點(diǎn)實(shí)現(xiàn)多個(gè)表的一致性讀取渺贤,不然的話雏胃,可能讀第一個(gè)表時(shí)请毛,其他表由于還沒(méi)進(jìn)行讀操作志鞍,沒(méi)有自動(dòng)加鎖,可能數(shù)據(jù)會(huì)發(fā)生改變方仿。并且顯示加鎖后只能訪問(wèn)加鎖的表固棚,不能訪問(wèn)其他表统翩。
MyISAM存儲(chǔ)引擎有個(gè)系統(tǒng)變量 concurrent_insert,專門用來(lái)控制并發(fā)插入的行為此洲,可以取 0 厂汗, 1 , 2呜师。
一般如果對(duì)并發(fā)要求比較高的情況下娶桦,可以設(shè)置為2,總是可以插入汁汗,然后定期在數(shù)據(jù)庫(kù)空閑時(shí)間對(duì)表進(jìn)行optimize衷畦。
需要注意的是,其中讀操作不會(huì)阻塞其他用戶對(duì)同一表的讀請(qǐng)求知牌,但會(huì)阻塞對(duì)同一表的寫請(qǐng)求祈争;并且當(dāng)寫鎖和讀鎖同時(shí)被申請(qǐng)時(shí),優(yōu)先獲得寫鎖角寸,這也這正是表級(jí)鎖發(fā)生鎖沖突概率最高的原因菩混,因?yàn)閷戞i可能會(huì)一直阻塞讀鎖,所以不適合有大量寫操作的環(huán)境下工作扁藕。這一問(wèn)題可以通過(guò)設(shè)置low-priority-updates這一啟動(dòng)參數(shù)來(lái)降低寫的優(yōu)先級(jí)沮峡。 雖然寫鎖優(yōu)先于讀鎖獲取,但是長(zhǎng)時(shí)間的查詢操作也可能會(huì)讓寫操作餓死亿柑,所以盡量避免一條SQL語(yǔ)句執(zhí)行所有的查詢帖烘,應(yīng)該進(jìn)行必要的分解。
由于InnoDB支持事務(wù)橄杨,并默認(rèn)是使用行級(jí)鎖秘症,所以InnoDB的鎖問(wèn)題和MyISAM鎖問(wèn)題還是有蠻大差別的。
共享鎖(S)和排他鎖(X)式矫,分別類似于MyISAM的讀鎖和寫鎖乡摹。對(duì)于 UPDATE、 DELETE 和 INSERT 語(yǔ)句采转,InnoDB會(huì)自動(dòng)給涉及數(shù)據(jù)集加排他鎖(X)聪廉;對(duì)于普通 SELECT 語(yǔ)句,InnoDB不會(huì)加任何鎖故慈。
可以顯式的加鎖板熊,用lock in share mode 顯式的加共享鎖,用 for update 顯式的加排他鎖察绷。
(3). InnoDB行鎖的實(shí)現(xiàn)方式——索引加鎖
(4). 間隙鎖
(5). 隔離級(jí)別與鎖
死鎖
在應(yīng)用中干签,可以通過(guò)如下方式來(lái)盡可能的避免死鎖:
(2) 在程序以批量方式處理數(shù)據(jù)時(shí),如果事先對(duì)數(shù)據(jù)排序拆撼,保證每個(gè)線程按固定的順序來(lái)處理記錄容劳,也可以大大的降低出現(xiàn)死鎖的可能喘沿。
秒殺一
秒殺二
秒殺三
Mysql innodb雖是鎖行的,但是如果沒(méi)有索引竭贩,或者索引唯一性不是特別強(qiáng)蚜印,那就要鎖表了。
鎖有兩種:悲觀鎖和樂(lè)觀鎖留量。悲觀鎖假定其他用戶企圖訪問(wèn)或者改變你正在訪問(wèn)窄赋、更改的對(duì)象的概率是很高的,因此在悲觀鎖的環(huán)境中楼熄,在你開(kāi)始改變此對(duì)象之前就將該對(duì)象鎖住寝凌,并且直到你提交了所作的更改之后才釋放鎖。悲觀的缺陷是不論是頁(yè)鎖還是行鎖孝赫,加鎖的時(shí)間可能會(huì)很長(zhǎng)较木,這樣可能會(huì)長(zhǎng)時(shí)間的限制其他用戶的訪問(wèn),也就是說(shuō)悲觀鎖的并發(fā)訪問(wèn)性不好青柄。
參考
https://www.cnblogs.com/liujiacai/p/7605612.html
MySQL主要的兩種鎖的特性可大致歸納如下:
考慮上述特點(diǎn)伐债,表級(jí)鎖使用與并發(fā)性不高,以查詢?yōu)橹髦驴倭扛碌膽?yīng)用峰锁,比如小型的web應(yīng)用;而行級(jí)鎖適用于高并發(fā)環(huán)境下双戳,對(duì)事務(wù)完整性要求較高的系統(tǒng)虹蒋,如在線事務(wù)處理系統(tǒng)。
(1). 鎖模式
(2). 如何加鎖
(3). 并發(fā)插入
0表示不允許并發(fā)插入飒货,1表示表中間沒(méi)有刪除的行時(shí)可以在表末尾插入魄衅,2表示總是可以插入。
(4). 鎖的調(diào)度
InnoDB鎖細(xì)述
(1). 鎖模式
(2). 如何加鎖
需要注意的是塘辅,如果線程A加了共享鎖后晃虫,線程B對(duì)同一個(gè)表加了共享鎖,那么兩個(gè)線程需要進(jìn)行更新操作時(shí)會(huì)產(chǎn)生死鎖扣墩。所以哲银,進(jìn)行更新操作時(shí)最好加排他鎖。
這一點(diǎn)與Oracle不同呻惕,所以這也意味著(重要):1. 只有通過(guò)索引條件檢索數(shù)據(jù)時(shí)荆责,InnoDB才會(huì)使用行級(jí)鎖,否則會(huì)使用表級(jí)鎖亚脆。 2. 即使是訪問(wèn)不同行的記錄做院,如果使用的是相同的索引鍵,會(huì)發(fā)生鎖沖突。 3. 如果數(shù)據(jù)表建有多個(gè)索引時(shí)山憨,可以通過(guò)不同的索引鎖定不同的行。
InnoDB支持事務(wù)弥喉,為了滿足隔離級(jí)別的要求郁竟,InnoDB有個(gè)間隙鎖,當(dāng)使用范圍查找時(shí)由境,InnoDB會(huì)給滿足key范圍要求棚亩,但實(shí)際并不存在的記錄加鎖。例如:select * from user where id > 100 for updata 會(huì)給ID>100的記錄加排他鎖虏杰,滿足這個(gè)范圍讥蟆,但不存在的記錄,會(huì)加間隙鎖纺阔,這樣可以避免幻讀瘸彤,避免讀取的時(shí)候插入滿足條件的記錄。
一般來(lái)說(shuō)笛钝,隔離級(jí)別越高质况,加鎖就越嚴(yán)格。這樣玻靡,產(chǎn)生鎖沖突的概率就越大结榄,一般實(shí)際應(yīng)用中,通過(guò)優(yōu)化應(yīng)用邏輯囤捻,選用 可提交讀 級(jí)別就夠了臼朗。對(duì)于一些確實(shí)需要更高隔離級(jí)別的事務(wù),再通過(guò)set session transaction isolation level+"級(jí)別" 來(lái)動(dòng)態(tài)改變滿足需求蝎土。
MyISAM是沒(méi)有死鎖問(wèn)題的视哑,因?yàn)樗麜?huì)一次性獲得所有的鎖。InnoDB發(fā)生死鎖后一般能自動(dòng)檢測(cè)到誊涯,并使一個(gè)事務(wù)釋放鎖并回退黎炉,另一個(gè)事務(wù)獲得鎖,繼續(xù)完成事務(wù)醋拧。
(1) 如果不同的程序會(huì)并發(fā)的存取多個(gè)表慷嗜,應(yīng)盡量約定以相同的順序來(lái)訪問(wèn)表,這樣可以大大降低產(chǎn)生死鎖的機(jī)會(huì)丹壕。
案例
查詢庫(kù)存庆械,由于是主鍵查詢使用到了索引,所以是行級(jí)鎖
SELECT number FROM seckill WHERE seckill_id=? FOR UPDATE
如果庫(kù)存大于秒殺數(shù)則更新菌赖,UPDATE 操作也是行級(jí)鎖
UPDATE seckill SET number=number-1 WHERE seckill_id=?
//直接更新數(shù)據(jù)缭乘,如果count為1秒殺成功否則失敗
UPDATE seckill SET number=number-1 WHERE seckill_id=? AND number>0
//獲取商品版本號(hào)以及剩余數(shù)量
SELECT version,number FROM seckill WHERE seckill_id=?
//判斷剩余數(shù)量是否充足并更新
UPDATE seckill SET number=number-?,version=version+1 WHERE seckill_id=? AND version = ?
//如果更新數(shù)量等于1秒殺成功否則失敗
加鎖對(duì)并發(fā)訪問(wèn)的影響體現(xiàn)在鎖的粒度上,可見(jiàn)行鎖粒度最小琉用,并發(fā)訪問(wèn)最好堕绩,頁(yè)鎖粒度最大策幼,表鎖介于2者之間。
與悲觀鎖相反奴紧,樂(lè)觀鎖則認(rèn)為其他用戶企圖改變你正在更改的對(duì)象的概率是很小的特姐,因此樂(lè)觀鎖直到你準(zhǔn)備提交所作的更改時(shí)才將對(duì)象鎖住,當(dāng)你讀取以及改變?cè)搶?duì)象時(shí)并不加鎖黍氮√坪可見(jiàn)樂(lè)觀鎖加鎖的時(shí)間要比悲觀鎖短,樂(lè)觀鎖可以用較大的鎖粒度獲得較好的并發(fā)訪問(wèn)性能沫浆。但是如果第二個(gè)用戶恰好在第一個(gè)用戶提交更改之前讀取了該對(duì)象捷枯,那么當(dāng)他完成了自己的更改進(jìn)行提交時(shí),數(shù)據(jù)庫(kù)就會(huì)發(fā)現(xiàn)該對(duì)象已經(jīng)變化了专执,這樣淮捆,第二個(gè)用戶不得不重新讀取該對(duì)象并作出更改。這說(shuō)明在樂(lè)觀鎖環(huán)境中本股,會(huì)增加并發(fā)用戶讀取對(duì)象的次數(shù)争剿。