面試命中率90%的點(diǎn):MySQL鎖
一、對(duì)MySQL的鎖的了解
當(dāng)數(shù)據(jù)庫有并發(fā)事務(wù)的時(shí)候,可能會(huì)產(chǎn)生數(shù)據(jù)的不一致,這時(shí)候需要一些機(jī)制來保證訪問的次序,鎖機(jī)制就是這樣的一個(gè)機(jī)制。
就像酒店的房間,如果大家隨意進(jìn)出,就會(huì)出現(xiàn)多人搶奪同一個(gè)房間的情況,而在房間上裝上鎖,申請(qǐng)到鑰匙的人才可以入住并且將房間鎖起來,其他人只有等他使用完畢才可以再次使用。
二、隔離級(jí)別與鎖的關(guān)系
在Read Uncommitted級(jí)別下,讀取數(shù)據(jù)不需要加共享鎖,這樣就不會(huì)跟被修改的數(shù)據(jù)上的排他鎖沖突
在Read Committed級(jí)別下,讀操作需要加共享鎖,但是在語句執(zhí)行完以后釋放共享鎖。
在Repeatable Read級(jí)別下,讀操作需要加共享鎖,但是在事務(wù)提交之前并不釋放共享鎖,也就是必須等待事務(wù)執(zhí)行完畢以后才釋放共享鎖。
SERIALIZABLE 是限制性最強(qiáng)的隔離級(jí)別,因?yàn)樵摷?jí)別鎖定整個(gè)范圍的鍵,并一直持有鎖,直到事務(wù)完成。
三、按照鎖的粒度分?jǐn)?shù)據(jù)庫鎖有哪些?鎖機(jī)制與InnoDB鎖算法
在關(guān)系型數(shù)據(jù)庫中,可以按照鎖的粒度把數(shù)據(jù)庫鎖分為行級(jí)鎖(INNODB引擎)、表級(jí)鎖(MYISAM引擎)和頁級(jí)鎖(BDB引擎 )。
- MyISAM和InnoDB存儲(chǔ)引擎使用的鎖:
MyISAM采用表級(jí)鎖(table-level locking)。
InnoDB支持行級(jí)鎖(row-level locking)和表級(jí)鎖,默認(rèn)為行級(jí)鎖。
- 行級(jí)鎖,表級(jí)鎖和頁級(jí)鎖對(duì)比
行級(jí)鎖:MySQL中鎖定粒度最細(xì)的一種鎖,表示只針對(duì)當(dāng)前操作的行進(jìn)行加鎖。行級(jí)鎖能大大減少數(shù)據(jù)庫操作的沖突。其加鎖粒度最小,但加鎖的開銷也最大。行級(jí)鎖分為共享鎖和排他鎖。
特點(diǎn):開銷大,加鎖慢;會(huì)出現(xiàn)死鎖;鎖定粒度最小,發(fā)生鎖沖突的概率最低,并發(fā)度也最高。
表級(jí)鎖:MySQL中鎖定粒度最大的一種鎖,表示對(duì)當(dāng)前操作的整張表加鎖,它實(shí)現(xiàn)簡單,資源消耗較少,被大部分MySQL引擎支持。最常使用的MyISAM與InnoDB都支持表級(jí)鎖定。表級(jí)鎖定分為表共享讀鎖(共享鎖)與表獨(dú)占寫鎖(排他鎖)。
特點(diǎn):開銷小,加鎖快;不會(huì)出現(xiàn)死鎖;鎖定粒度大,發(fā)出鎖沖突的概率最高,并發(fā)度最低。
頁級(jí)鎖:是MySQL中鎖定粒度介于行級(jí)鎖和表級(jí)鎖中間的一種鎖。表級(jí)鎖速度快,但沖突多,行級(jí)沖突少,但速度慢。所以取了折衷的頁級(jí),一次鎖定相鄰的一組記錄。
特點(diǎn):開銷和加鎖時(shí)間界于表鎖和行鎖之間;會(huì)出現(xiàn)死鎖;鎖定粒度界于表鎖和行鎖之間,并發(fā)度一般。
四、從鎖的類別上分MySQL都有哪些鎖呢?像上面那樣子進(jìn)行鎖定豈不是有點(diǎn)阻礙并發(fā)效率了
從鎖的類別上來講,有共享鎖和排他鎖。
共享鎖: 又叫做讀鎖。當(dāng)用戶要進(jìn)行數(shù)據(jù)的讀取時(shí),對(duì)數(shù)據(jù)加上共享鎖。共享鎖可以同時(shí)加上多個(gè)。
排他鎖: 又叫做寫鎖,當(dāng)用戶要進(jìn)行數(shù)據(jù)的寫入時(shí),對(duì)數(shù)據(jù)加上排他鎖。排他鎖只可以加一個(gè),他和其他的排他鎖,共享鎖都相斥。
用上面的例子來說就是用戶的行為有兩種,一種是來看房,多個(gè)用戶一起看房是可以接受的。一種是真正的入住一晚,在這期間,無論是想入住的還是想看房的都不可以。
鎖的粒度取決于具體的存儲(chǔ)引擎,InnoDB實(shí)現(xiàn)了行級(jí)鎖,頁級(jí)鎖,表級(jí)鎖。
他們的加鎖開銷從大到小,并發(fā)能力也是從大到小。
五、MySQL中InnoDB引擎的行鎖是怎么實(shí)現(xiàn)的?
InnoDB是基于索引來完成行鎖
例: select * from tab_with_index where id = 1 for update;
for update 可以根據(jù)條件來完成行鎖鎖定,并且 ID 是有索引鍵的列,如果 ID不是索引鍵那么InnoDB將完成表鎖,并發(fā)將無從談起
六、InnoDB存儲(chǔ)引擎的鎖的算法有三種
1.Record lock:單個(gè)行記錄上的鎖
2.Gap lock:間隙鎖,鎖定一個(gè)范圍,不包括記錄本身
3.Next-key lock:record+gap 鎖定一個(gè)范圍,包含記錄本身
七、相關(guān)知識(shí)點(diǎn):
Innodb對(duì)于行的查詢使用next-key lock
Next-locking keying為了解決Phantom Problem幻讀問題
當(dāng)查詢的索引含有唯一屬性時(shí),將next-key lock降級(jí)為record key
Gap鎖設(shè)計(jì)的目的是為了阻止多個(gè)事務(wù)將記錄插入到同一范圍內(nèi),而這會(huì)導(dǎo)致幻讀問題的產(chǎn)生
有兩種方式顯式關(guān)閉gap鎖:(除了外鍵約束和唯一性檢查外,其余情況僅使用record lock) A. 將事務(wù)隔離級(jí)別設(shè)置為RCB. 將參數(shù)innodb_locks_unsafe_for_binlog設(shè)置為1
八、什么是死鎖?怎么解決?死鎖是指兩個(gè)或多個(gè)事務(wù)在同一資源上相互占用,并請(qǐng)求鎖定對(duì)方的資源,從而導(dǎo)致惡性循環(huán)的現(xiàn)象。
常見的解決死鎖的方法:
1、如果不同程序會(huì)并發(fā)存取多個(gè)表,盡量約定以相同的順序訪問表,可以大大降低死鎖機(jī)會(huì)。
2、在同一個(gè)事務(wù)中,盡可能做到一次鎖定所需要的所有資源,減少死鎖產(chǎn)生概率;
3、對(duì)于非常容易產(chǎn)生死鎖的業(yè)務(wù)部分,可以嘗試使用升級(jí)鎖定顆粒度,通過表級(jí)鎖定來減少死鎖產(chǎn)生的概率;
如果業(yè)務(wù)處理不好可以用分布式事務(wù)鎖或者使用樂觀鎖
九、數(shù)據(jù)庫的樂觀鎖和悲觀鎖是什么?怎么實(shí)現(xiàn)的?
數(shù)據(jù)庫管理系統(tǒng)(DBMS)中的并發(fā)控制的任務(wù)是確保在多個(gè)事務(wù)同時(shí)存取數(shù)據(jù)庫中同一數(shù)據(jù)時(shí)不破壞事務(wù)的隔離性和統(tǒng)一性以及數(shù)據(jù)庫的統(tǒng)一性。樂觀并發(fā)控制(樂觀鎖)和悲觀并發(fā)控制(悲觀鎖)是并發(fā)控制主要采用的技術(shù)手段。
悲觀鎖:假定會(huì)發(fā)生并發(fā)沖突,屏蔽一切可能違反數(shù)據(jù)完整性的操作。在查詢完數(shù)據(jù)的時(shí)候就把事務(wù)鎖起來,直到提交事務(wù)。實(shí)現(xiàn)方式:使用數(shù)據(jù)庫中的鎖機(jī)制
樂觀鎖:假設(shè)不會(huì)發(fā)生并發(fā)沖突,只在提交操作時(shí)檢查是否違反數(shù)據(jù)完整性。在修改數(shù)據(jù)的時(shí)候把事務(wù)鎖起來,通過Version的方式來進(jìn)行鎖定。實(shí)現(xiàn)方式:一般會(huì)使用版本號(hào)機(jī)制或CAS算法實(shí)現(xiàn)。
兩種鎖的使用場(chǎng)景
從上面對(duì)兩種鎖的介紹,我們知道兩種鎖各有優(yōu)缺點(diǎn),不可認(rèn)為一種好于另一種,像樂觀鎖適用于寫比較少的情況下(多讀場(chǎng)景),即沖突真的很少發(fā)生的時(shí)候,這樣可以省去了鎖的開銷,加大了系統(tǒng)的整個(gè)吞吐量。
但如果是多寫的情況,一般會(huì)經(jīng)常產(chǎn)生沖突,這就會(huì)導(dǎo)致上層應(yīng)用會(huì)不斷的進(jìn)行Retry,這樣反倒是降低了性能,所以一般多寫的場(chǎng)景下用悲觀鎖就比較合適。