這篇文章主要介紹了MySQL鎖機(jī)制的示例分析,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
創(chuàng)新互聯(lián)公司始終堅(jiān)持【策劃先行,效果至上】的經(jīng)營(yíng)理念,通過(guò)多達(dá)10余年累計(jì)超上千家客戶(hù)的網(wǎng)站建設(shè)總結(jié)了一套系統(tǒng)有效的全網(wǎng)營(yíng)銷(xiāo)推廣解決方案,現(xiàn)已廣泛運(yùn)用于各行各業(yè)的客戶(hù),其中包括:電動(dòng)窗簾等企業(yè),備受客戶(hù)贊揚(yáng)。
鎖在MySQL中是非常重要的一部分,鎖對(duì)MySQL的數(shù)據(jù)訪(fǎng)問(wèn)并發(fā)有著舉足輕重的影響。鎖涉及到的知識(shí)篇幅也很多,所以要啃完并消化到自己的肚子里,是需要靜下心好好反反復(fù)復(fù)幾遍地細(xì)細(xì)品味。本文是對(duì)鎖的一個(gè)大概的整理,一些相關(guān)深入的細(xì)節(jié),還是需要找到相關(guān)書(shū)籍來(lái)繼續(xù)夯實(shí)。
鎖的認(rèn)識(shí)
1.1 鎖的解釋
計(jì)算機(jī)協(xié)調(diào)多個(gè)進(jìn)程或線(xiàn)程并發(fā)訪(fǎng)問(wèn)某一資源的機(jī)制。
1.2 鎖的重要性
在數(shù)據(jù)庫(kù)中,除傳統(tǒng)計(jì)算資源(CPU、RAM、I\O等)的爭(zhēng)搶?zhuān)瑪?shù)據(jù)也是一種供多用戶(hù)共享的資源。 如何保證數(shù)據(jù)并發(fā)訪(fǎng)問(wèn)的一致性,有效性,是所有數(shù)據(jù)庫(kù)必須要解決的問(wèn)題。 鎖沖突也是影響數(shù)據(jù)庫(kù)并發(fā)訪(fǎng)問(wèn)性能的一個(gè)重要因素,因此鎖對(duì)數(shù)據(jù)庫(kù)尤其重要。
1.3 鎖的缺點(diǎn)
加鎖是消耗資源的,鎖的各種操作,包括獲得鎖、檢測(cè)鎖是否已解除、釋放鎖等 ,都會(huì)增加系統(tǒng)的開(kāi)銷(xiāo)。
1.4 簡(jiǎn)單的例子
現(xiàn)如今網(wǎng)購(gòu)已經(jīng)特別普遍了,比如淘寶雙十一活動(dòng),當(dāng)天的人流量是千萬(wàn)及億級(jí)別的,但商家的庫(kù)存是有限的。 系統(tǒng)為了保證商家的商品庫(kù)存不發(fā)生超賣(mài)現(xiàn)象,會(huì)對(duì)商品的庫(kù)存進(jìn)行鎖控制。當(dāng)有用戶(hù)正在下單某款商品最后一件時(shí), 系統(tǒng)會(huì)立馬對(duì)該件商品進(jìn)行鎖定,防止其他用戶(hù)也重復(fù)下單,直到支付動(dòng)作完成才會(huì)釋放(支付成功則立即減庫(kù)存售罄,支付失敗則立即釋放)。
鎖的類(lèi)型
2.1 表鎖
種類(lèi)
讀鎖(read lock),也叫共享鎖(shared lock) 針對(duì)同一份數(shù)據(jù),多個(gè)讀操作可以同時(shí)進(jìn)行而不會(huì)互相影響(select)
寫(xiě)鎖(write lock),也叫排他鎖(exclusive lock) 當(dāng)前操作沒(méi)完成之前,會(huì)阻塞其它讀和寫(xiě)操作(update、insert、delete)
存儲(chǔ)引擎默認(rèn)鎖
MyISAM
特點(diǎn)
1. 對(duì)整張表加鎖 2. 開(kāi)銷(xiāo)小 3. 加鎖快 4. 無(wú)死鎖 5. 鎖粒度大,發(fā)生鎖沖突概率大,并發(fā)性低
1. 讀鎖會(huì)阻塞寫(xiě)操作,不會(huì)阻塞讀操作 2. 寫(xiě)鎖會(huì)阻塞讀和寫(xiě)操作
MyISAM的讀寫(xiě)鎖調(diào)度是寫(xiě)優(yōu)先,這也是MyISAM不適合做寫(xiě)為主表的引擎,因?yàn)閷?xiě)鎖以后,其它線(xiàn)程不能做任何操作,大量的更新使查詢(xún)很難得到鎖,從而造成永遠(yuǎn)阻塞。
2.2 行鎖
種類(lèi)
讀鎖(read lock),也叫共享鎖(shared lock) 允許一個(gè)事務(wù)去讀一行,阻止其他事務(wù)獲得相同數(shù)據(jù)集的排他鎖
寫(xiě)鎖(write lock),也叫排他鎖(exclusive lock) 允許獲得排他鎖的事務(wù)更新數(shù)據(jù),阻止其他事務(wù)取得相同數(shù)據(jù)集的共享鎖和排他鎖
意向共享鎖(IS) 一個(gè)事務(wù)給一個(gè)數(shù)據(jù)行加共享鎖時(shí),必須先獲得表的IS鎖
意向排它鎖(IX) 一個(gè)事務(wù)給一個(gè)數(shù)據(jù)行加排他鎖時(shí),必須先獲得該表的IX鎖
InnoDB
1. 對(duì)一行數(shù)據(jù)加鎖 2. 開(kāi)銷(xiāo)大 3. 加鎖慢 4. 會(huì)出現(xiàn)死鎖 5. 鎖粒度小,發(fā)生鎖沖突概率最低,并發(fā)性高
1. 更新丟失 解決:讓事務(wù)變成串行操作,而不是并發(fā)的操作,即對(duì)每個(gè)事務(wù)開(kāi)始---對(duì)讀取記錄加排他鎖 2. 臟讀 解決:隔離級(jí)別為Read uncommitted 3. 不可重讀 解決:使用Next-Key Lock算法來(lái)避免 4. 幻讀 解決:間隙鎖(Gap Lock)
開(kāi)銷(xiāo)、加鎖時(shí)間和鎖粒度介于表鎖和行鎖之間,會(huì)出現(xiàn)死鎖,并發(fā)處理能力一般(此鎖不做多介紹)
如何上鎖?
3.1 表鎖
select //上讀鎖
insert、update、delete //上寫(xiě)鎖
lock table tableName read;//讀鎖 lock table tableName write;//寫(xiě)鎖
unlock tables;//所有鎖表
session01 | session02 |
---|---|
lock table teacher read;//上讀鎖 | |
select * from teacher; //可以正常讀取 | select * from teacher;//可以正常讀取 |
update teacher set name = 3 where id =2;//報(bào)錯(cuò)因被上讀鎖不能寫(xiě)操作 | update teacher set name = 3 where id =2;//被阻塞 |
unlock tables;//解鎖 | |
update teacher set name = 3 where id =2;//更新操作成功 |
session01 | session02 |
---|---|
lock table teacher write;//上寫(xiě)鎖 | |
select * from teacher; //可以正常讀取 | select * from teacher;//被阻塞 |
update teacher set name = 3 where id =2;//可以正常更新操作 | update teacher set name = 4 where id =2;//被阻塞 |
unlock tables;//解鎖 | |
select * from teacher;//讀取成功 | |
update teacher set name = 4 where id =2;//更新操作成功 |
3.2 行鎖
隱式上鎖(默認(rèn),自動(dòng)加鎖自動(dòng)釋放)
select //不會(huì)上鎖
insert、update、delete //上寫(xiě)鎖
select * from tableName lock in share mode;//讀鎖 select * from tableName for update;//寫(xiě)鎖
1. 提交事務(wù)(commit) 2. 回滾事務(wù)(rollback) 3. kill 阻塞進(jìn)程
session01 | session02 |
---|---|
begin; | |
select * from teacher where id = 2 lock in share mode;//上讀鎖 | |
select * from teacher where id = 2;//可以正常讀取 | |
update teacher set name = 3 where id =2;// 可以更新操作 | update teacher set name = 5 where id =2;//被阻塞 |
commit; | |
update teacher set name = 5 where id =2;//更新操作成功 |
session01 | session02 |
---|---|
begin; | |
select * from teacher where id = 2 for update;//上寫(xiě)鎖 | |
select * from teacher where id = 2;//可以正常讀取 | |
update teacher set name = 3 where id =2;// 可以更新操作 | update teacher set name = 5 where id =2;//被阻塞 |
rollback; | |
update teacher set name = 5 where id =2;//更新操作成功 |
為什么上了寫(xiě)鎖,別的事務(wù)還可以讀操作? 因?yàn)镮nnoDB有MVCC機(jī)制(多版本并發(fā)控制),可以使用快照讀,而不會(huì)被阻塞。
行鎖的實(shí)現(xiàn)算法
4.1 Record Lock鎖
單個(gè)行記錄上的鎖 Record Lock總是會(huì)去鎖住索引記錄,如果InnoDB存儲(chǔ)引擎表建立的時(shí)候沒(méi)有設(shè)置任何一個(gè)索引,這時(shí)InnoDB存儲(chǔ)引擎會(huì)使用隱式的主鍵來(lái)進(jìn)行鎖定
4.2 Gap Lock鎖
當(dāng)我們用范圍條件而不是相等條件檢索數(shù)據(jù),并請(qǐng)求共享或排他鎖時(shí),InnoDB會(huì)給符合條件的已有數(shù)據(jù)記錄的索引加鎖,對(duì)于鍵值在條件范圍內(nèi)但并不存在的記錄。 優(yōu)點(diǎn):解決了事務(wù)并發(fā)的幻讀問(wèn)題 不足:因?yàn)閝uery執(zhí)行過(guò)程中通過(guò)范圍查找的話(huà),他會(huì)鎖定爭(zhēng)個(gè)范圍內(nèi)所有的索引鍵值,即使這個(gè)鍵值并不存在。 間隙鎖有一個(gè)致命的弱點(diǎn),就是當(dāng)鎖定一個(gè)范圍鍵值之后,即使某些不存在的鍵值也會(huì)被無(wú)辜的鎖定,而造成鎖定的時(shí)候無(wú)法插入鎖定鍵值范圍內(nèi)任何數(shù)據(jù)。在某些場(chǎng)景下這可能會(huì)對(duì)性能造成很大的危害。
4.3 Next-key Lock鎖
同時(shí)鎖住數(shù)據(jù)+間隙鎖 在Repeatable Read隔離級(jí)別下,Next-key Lock 算法是默認(rèn)的行記錄鎖定算法。
4.4 行鎖的注意點(diǎn)
1. 只有通過(guò)索引條件檢索數(shù)據(jù)時(shí),InnoDB才會(huì)使用行級(jí)鎖,否則會(huì)使用表級(jí)鎖(索引失效,行鎖變表鎖) 2. 即使是訪(fǎng)問(wèn)不同行的記錄,如果使用的是相同的索引鍵,會(huì)發(fā)生鎖沖突 3. 如果數(shù)據(jù)表建有多個(gè)索引時(shí),可以通過(guò)不同的索引鎖定不同的行
如何排查鎖?
5.1 表鎖
show open tables;
show status like 'table%';
1. table_locks_waited 出現(xiàn)表級(jí)鎖定爭(zhēng)用而發(fā)生等待的次數(shù)(不能立即獲取鎖的次數(shù),每等待一次值加1),此值高說(shuō)明存在著較嚴(yán)重的表級(jí)鎖爭(zhēng)用情況 2. table_locks_immediate 產(chǎn)生表級(jí)鎖定次數(shù),不是可以立即獲取鎖的查詢(xún)次數(shù),每立即獲取鎖加1
5.2 行鎖
行鎖分析
show status like 'innodb_row_lock%';
1. innodb_row_lock_current_waits //當(dāng)前正在等待鎖定的數(shù)量 2. innodb_row_lock_time //從系統(tǒng)啟動(dòng)到現(xiàn)在鎖定總時(shí)間長(zhǎng)度 3. innodb_row_lock_time_avg //每次等待所花平均時(shí)間 4. innodb_row_lock_time_max //從系統(tǒng)啟動(dòng)到現(xiàn)在等待最長(zhǎng)的一次所花時(shí)間 5. innodb_row_lock_waits //系統(tǒng)啟動(dòng)后到現(xiàn)在總共等待的次數(shù)
1. innodb_lock_waits表 2. innodb_locks表 3. innodb_trx表
1. 盡可能讓所有數(shù)據(jù)檢索都通過(guò)索引來(lái)完成,避免無(wú)索引行鎖升級(jí)為表鎖 2. 合理設(shè)計(jì)索引,盡量縮小鎖的范圍 3. 盡可能較少檢索條件,避免間隙鎖 4. 盡量控制事務(wù)大小,減少鎖定資源量和時(shí)間長(zhǎng)度 5. 盡可能低級(jí)別事務(wù)隔離
死鎖
6.1 解釋
指兩個(gè)或者多個(gè)事務(wù)在同一資源上相互占用,并請(qǐng)求鎖定對(duì)方占用的資源,從而導(dǎo)致惡性循環(huán)的現(xiàn)象
6.2 產(chǎn)生的條件
1. 互斥條件:一個(gè)資源每次只能被一個(gè)進(jìn)程使用 2. 請(qǐng)求與保持條件:一個(gè)進(jìn)程因請(qǐng)求資源而阻塞時(shí),對(duì)已獲得的資源保持不放 3. 不剝奪條件:進(jìn)程已獲得的資源,在沒(méi)有使用完之前,不能強(qiáng)行剝奪 4. 循環(huán)等待條件:多個(gè)進(jìn)程之間形成的一種互相循環(huán)等待的資源的關(guān)系
6.1 解決
1. 查看死鎖:show engine innodb status \G 2. 自動(dòng)檢測(cè)機(jī)制,超時(shí)自動(dòng)回滾代價(jià)較小的事務(wù)(innodb_lock_wait_timeout 默認(rèn)50s) 3. 人為解決,kill阻塞進(jìn)程(show processlist) 4. wait for graph 等待圖(主動(dòng)檢測(cè))
6.1 如何避免
1. 加鎖順序一致,盡可能一次性鎖定所需的數(shù)據(jù)行 2. 盡量基于primary(主鍵)或unique key更新數(shù)據(jù) 3. 單次操作數(shù)據(jù)量不宜過(guò)多,涉及表盡量少 4. 減少表上索引,減少鎖定資源 5. 盡量使用較低的隔離級(jí)別 6. 盡量使用相同條件訪(fǎng)問(wèn)數(shù)據(jù),這樣可以避免間隙鎖對(duì)并發(fā)的插入影響 7. 精心設(shè)計(jì)索引,盡量使用索引訪(fǎng)問(wèn)數(shù)據(jù) 8. 借助相關(guān)工具:pt-deadlock-logger
樂(lè)觀(guān)鎖與悲觀(guān)鎖
7.1 悲觀(guān)鎖
假定會(huì)發(fā)生并發(fā)沖突,屏蔽一切可能違反數(shù)據(jù)完整性的操作
表鎖、行鎖等
數(shù)據(jù)庫(kù)本身
并發(fā)量大
7.2 樂(lè)觀(guān)鎖
假設(shè)不會(huì)發(fā)生并發(fā)沖突,只在提交操作時(shí)檢查是否違反數(shù)據(jù)完整性
提交更新時(shí)檢查版本號(hào)或者時(shí)間戳是否符合
業(yè)務(wù)代碼
并發(fā)量小
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“MySQL鎖機(jī)制的示例分析”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!