一、存儲(chǔ)引擎中鎖的區(qū)別
在介紹鎖之前,我們先來(lái)了解下mysql的存儲(chǔ)引擎。我們常用的存儲(chǔ)引擎一般有兩種,MyISAM和InnoDB,其中InnoDB最常用。至于兩種存儲(chǔ)引擎的特性我們就不在這里展開了,我們來(lái)看一下兩種存儲(chǔ)引擎里的鎖有什么不同呢?
1、MyISAM
MyISAM里是表鎖的形式。所謂表鎖就是對(duì)整張表進(jìn)行加鎖,優(yōu)點(diǎn)是它的開銷小,加鎖快;鎖定粒度大,并且不會(huì)發(fā)生死鎖。缺點(diǎn)是因?yàn)槭菍?duì)整張表進(jìn)行加鎖的,所以發(fā)生鎖沖突的概率也是最高的,并發(fā)度也比較低。
表鎖又分為共享鎖(讀鎖)和排它鎖(寫鎖);共享鎖又稱為讀鎖,也就是說(shuō)針對(duì)同一個(gè)表操作,如果兩個(gè)請(qǐng)求都是讀請(qǐng)求的情況下,是不會(huì)互相影響的。但是如果其中一個(gè)是寫操作的話,則會(huì)阻塞其他的寫操作或讀操作,直到鎖釋放為止。
默認(rèn)情況下,寫鎖比讀鎖具有更高的優(yōu)先級(jí):當(dāng)一個(gè)鎖釋放時(shí),這個(gè)鎖會(huì)優(yōu)先給寫鎖隊(duì)列中等候的獲取鎖請(qǐng)求,然后再給讀鎖隊(duì)列中等候的獲取鎖請(qǐng)求。這也正是 MyISAM 表不太適合于有大量更新操作和查詢操作應(yīng)用的原因,因?yàn)?,大量的更新操作?huì)造成查詢操作很難獲得讀鎖,從而可能永遠(yuǎn)阻塞。
2.表級(jí)鎖加鎖方式
了解了表鎖后,我們來(lái)看一下MyISAM是如何進(jìn)行加鎖的呢?其實(shí)MyISAM 在執(zhí)行查詢語(yǔ)句(SELECT)前,會(huì)自動(dòng)給涉及的表加讀鎖,在執(zhí)行寫操作
(UPDATE、DELETE、INSERT 等)前,會(huì)自動(dòng)給涉及的表加寫鎖,這個(gè)過(guò)程并不需要用戶干預(yù)。當(dāng)然,mysql也提供了LOCK TABLE 命令給 MyISAM 表顯式加鎖。在自動(dòng)加鎖的情況下,MyISAM 總是一次獲得 SQL 語(yǔ)句所需要的全部鎖,這也正是 MyISAM 表不會(huì)出現(xiàn)死鎖的原因。
3.InnoDB
說(shuō)完了MyISAM,我們來(lái)說(shuō)下InnoDB,InnoDB中既有表鎖也有行鎖。其中行鎖也是分為共享鎖(讀鎖)和排它鎖(寫鎖),只不過(guò)不同于表鎖的是行鎖的鎖是作用于行數(shù)據(jù),而不是作用于整張表。除此之外InnoDB還有意向共享鎖和意向排它鎖,意向鎖也是表級(jí)鎖,為什么會(huì)有這兩種鎖呢?
由于表鎖和行鎖雖然鎖定范圍不同,但是會(huì)相互沖突。所以當(dāng)你要加表鎖時(shí),勢(shì)必要先遍歷該表的所有記錄,判斷是否加有排他鎖。這種遍歷檢查的方式顯然是一種低效的方式,MySQL 引入了意向鎖,來(lái)檢測(cè)表鎖和行鎖的沖突。
當(dāng)事務(wù)要在記錄上加上讀鎖或?qū)戞i時(shí),要首先在表上加上意向鎖。這樣判斷表中是否有記錄加鎖就很簡(jiǎn)單了,只要看下表上是否有意向鎖就行了。意向共享鎖可以同時(shí)并存多個(gè),但是意向排他鎖同時(shí)只能有一個(gè)存在。意向鎖是InnoDB自動(dòng)加的,不需要用戶干預(yù)。
4.行級(jí)鎖加鎖方式
共享鎖:
對(duì)于普通的查詢語(yǔ)句是不會(huì)加鎖的,列如 select * from user;
對(duì)于有查詢條件的語(yǔ)句,如果查詢條件中有索引列,則使用行鎖,如果查詢條件非索引列,則使用表鎖。
顯示加鎖:SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
排它鎖:
對(duì)于UPDATE、DELETE和INSERT語(yǔ)句,InnoDB會(huì)自動(dòng)給涉及數(shù)據(jù)集加排他鎖;
顯示加鎖:SELECT * FROM table_name WHERE ... FOR UPDATE
5.間隙鎖
間隙鎖產(chǎn)生的原因是因?yàn)镼uery執(zhí)行過(guò)程中通過(guò)范圍查找的話,它會(huì)鎖定整個(gè)范圍內(nèi)所有的索引鍵值,即使這個(gè)鍵值并不存在。
例:假如user表中只有101條記錄,其id的值分別是 1,2,…,100,101,下面的SQL:
mysql> select * from user where id > 100 for update;
是一個(gè)范圍條件的檢索,InnoDB不僅會(huì)對(duì)符合條件的id值為101的記錄加鎖,也會(huì)對(duì)id大于101(這些記錄并不存在)的“間隙”加鎖。當(dāng)然間隙鎖的好處主要是防止防幻讀。此時(shí)如果還能插入一條102的數(shù)據(jù),則會(huì)導(dǎo)致幻讀。缺點(diǎn)是在插入頻繁的操作使用不當(dāng)會(huì)造成大量阻塞,所以我們要盡量避免這種范圍條件。
6.死鎖
-
死鎖產(chǎn)生:
- 死鎖是指兩個(gè)或多個(gè)事務(wù)在同一資源上相互占用,并請(qǐng)求鎖定對(duì)方占用的資源,從而導(dǎo)致惡性循環(huán)。
- 當(dāng)事務(wù)試圖以不同的順序鎖定資源時(shí),就可能產(chǎn)生死鎖。多個(gè)事務(wù)同時(shí)鎖定同一個(gè)資源時(shí)也可能會(huì)產(chǎn)生死鎖。
- 鎖的行為和順序和存儲(chǔ)引擎相關(guān)。以同樣的順序執(zhí)行語(yǔ)句,有些存儲(chǔ)引擎會(huì)產(chǎn)生死鎖有些不會(huì)——死鎖有雙重原因:真正的數(shù)據(jù)沖突;存儲(chǔ)引擎的實(shí)現(xiàn)方式。
- 檢測(cè)死鎖:數(shù)據(jù)庫(kù)系統(tǒng)實(shí)現(xiàn)了各種死鎖檢測(cè)和死鎖超時(shí)的機(jī)制。InnoDB存儲(chǔ)引擎能檢測(cè)到死鎖的循環(huán)依賴并立即返回一個(gè)錯(cuò)誤。
- 死鎖恢復(fù):死鎖發(fā)生以后,只有部分或完全回滾其中一個(gè)事務(wù),才能打破死鎖,InnoDB目前處理死鎖的方法是,將持有最少行級(jí)排他鎖的事務(wù)進(jìn)行回滾。所以事務(wù)型應(yīng)用程序在設(shè)計(jì)時(shí)必須考慮如何處理死鎖,多數(shù)情況下只需要重新執(zhí)行因死鎖回滾的事務(wù)即可。
- 外部鎖的死鎖檢測(cè):發(fā)生死鎖后,InnoDB 一般都能自動(dòng)檢測(cè)到,并使一個(gè)事務(wù)釋放鎖并回退,另一個(gè)事務(wù)獲得鎖,繼續(xù)完成事務(wù)。但在涉及外部鎖,或涉及表鎖的情況下,InnoDB 并不能完全自動(dòng)檢測(cè)到死鎖, 這需要通過(guò)設(shè)置鎖等待超時(shí)參數(shù) innodb_lock_wait_timeout 來(lái)解決
- 死鎖影響性能:死鎖會(huì)影響性能而不是會(huì)產(chǎn)生嚴(yán)重錯(cuò)誤,因?yàn)镮nnoDB會(huì)自動(dòng)檢測(cè)死鎖狀況并回滾其中一個(gè)受影響的事務(wù)。在高并發(fā)系統(tǒng)上,當(dāng)許多線程等待同一個(gè)鎖時(shí),死鎖檢測(cè)可能導(dǎo)致速度變慢。 有時(shí)當(dāng)發(fā)生死鎖時(shí),禁用死鎖檢測(cè)(使用innodb_deadlock_detect配置選項(xiàng))可能會(huì)更有效,這時(shí)可以依賴innodb_lock_wait_timeout設(shè)置進(jìn)行事務(wù)回滾。
- 死鎖預(yù)防:可以盡量在不同的事物中指定操作順序,達(dá)到事物順序一致,來(lái)避免多個(gè)事物同一資源的互相占用