mysql 锁怎么使用_Mysql锁一般使用
一些很小的項(xiàng)目一般不會(huì)特意使用或注意數(shù)據(jù)鎖,其實(shí)在事務(wù)操作修改與刪除時(shí)就已經(jīng)有隱式加鎖。一般所有涉及到共享數(shù)據(jù)都會(huì)考慮下數(shù)據(jù)的原始性問(wèn)題,保證數(shù)據(jù)在使用或修改時(shí)原始性沒(méi)有被破壞就需要鎖定數(shù)據(jù)所有權(quán);除非任何時(shí)刻同一時(shí)間只有一個(gè)進(jìn)程在運(yùn)行,但這種業(yè)務(wù)非常少。鎖會(huì)增加性能開(kāi)銷,使用不合理容易影響項(xiàng)目性能甚至?xí)斐伤梨i。
Mysql數(shù)據(jù)庫(kù)鎖受引擎影響,不同的引擎鎖的方式不一樣。常用的引擎:MyISAM引擎是僅支持表鎖,InnoDB引擎支持表鎖或索引鎖(行鎖)。MyISAM引擎已經(jīng)不再推薦使用,主是要因?yàn)镸yISAM引擎不支持事務(wù)雖然查詢性能略微高點(diǎn),但I(xiàn)nnoDB經(jīng)過(guò)幾個(gè)版本的升級(jí)后各方面已經(jīng)有很大的提升,其中在MySQL5.6版本后InnoDB開(kāi)始支持全文索引,到5.7后可以使用全文索引分詞插件(不過(guò)全文索引在關(guān)系型數(shù)據(jù)庫(kù)中使用并不多,一般會(huì)放到更專業(yè)的ES上)。
MySQL還有DBD引擎支持頁(yè)面鎖或表鎖,它可以代替InnoDB引擎,但這個(gè)引擎很少在MySQL中使用。
數(shù)據(jù)庫(kù)鎖會(huì)影響的操作有:修改、刪除、插入(數(shù)據(jù)、字段、索引)和修改、刪除表;
MySQL有防死鎖功能,一般業(yè)務(wù)程序造成的死鎖數(shù)據(jù)庫(kù)會(huì)自動(dòng)結(jié)束與之死鎖的會(huì)話,比如:會(huì)話A先鎖定表T1然后再鎖定表T2,但同時(shí)會(huì)話B先鎖定了表T2然后再去鎖定表T1,這個(gè)時(shí)候數(shù)據(jù)庫(kù)會(huì)認(rèn)定為死鎖,會(huì)保留最后一個(gè)加鎖引起死鎖的會(huì)話結(jié)束引起死鎖的其它會(huì)話連接。一般會(huì)報(bào)如下錯(cuò)誤:
Deadlock found when trying to get lock; try restarting transaction
MySQL加鎖方式
MySQL的鎖是針對(duì)會(huì)話連接進(jìn)程的,當(dāng)連接斷開(kāi)退出鎖將自動(dòng)解除,就如同事務(wù)一樣如果連接斷開(kāi)前未提交或回滾就退出后則事務(wù)自動(dòng)回滾(比如PHP程序異常后就會(huì)自動(dòng)斷開(kāi)連接并釋放鎖和回滾事務(wù))。不論是使用鎖或事務(wù)都不建議在期間做過(guò)多非鎖或事務(wù)必需工作,比如數(shù)據(jù)驗(yàn)證、加工、過(guò)濾、拼裝,查詢等操作,因?yàn)檫@些操作會(huì)增加鎖或事務(wù)的有效時(shí)間進(jìn)而增加其它會(huì)話等待時(shí)間導(dǎo)致影響整個(gè)系統(tǒng)的吞吐量。一般建議在鎖或事務(wù)期間盡量做對(duì)數(shù)據(jù)庫(kù)的一次性讀寫(xiě)操作。
表鎖
表鎖即全表鎖定不允許其它會(huì)話進(jìn)程對(duì)鎖定表做指定的操作,主要是不可讀鎖和不可寫(xiě)鎖操作,這種鎖常用的引擎均支持。這種鎖只有業(yè)務(wù)強(qiáng)烈要求才會(huì)使用到,絕多數(shù)項(xiàng)目不會(huì)使用此種鎖。使用這種鎖后其它會(huì)話的連接進(jìn)程再使用鎖定操作時(shí)將進(jìn)入等待狀態(tài),如果鎖一直不能釋放(或釋放不及時(shí))將會(huì)造成系統(tǒng)癱瘓。表級(jí)鎖,加鎖快。
加表鎖
通常SQL語(yǔ)法
lock table[s] table_name [write|read][, table_name1 [write|read] ...]
同一個(gè)會(huì)話連接只有最后一次表鎖SQL有效,前面加的鎖會(huì)在下一個(gè)加鎖SQL中自動(dòng)釋放,所以加鎖只會(huì)一次即可,否則易容造成鎖失效。加鎖后所有其它會(huì)話在做鎖定操作后會(huì)無(wú)期限的等待。
示例:
查看加鎖
show open tables where in_use > 0;
解表鎖
通常SQL語(yǔ)法
unlock table[s]
解鎖不需要指定表,直接是解除所有當(dāng)前會(huì)話加的表鎖,僅限使用lock加的鎖。
查看解鎖
索引鎖(行鎖)
行鎖是比較理想的鎖,把需要操作的記錄行給鎖定不影響其它行的操作,可以增加更多的并行操作空間。MySQL的InnoDB引擎支持的行鎖是在索引的基礎(chǔ)上加鎖,所以也可以說(shuō)InnoDB的鎖是索引鎖,如果加鎖時(shí)沒(méi)有索引或索引沒(méi)有命中那就會(huì)進(jìn)入到表鎖。索引鎖是通過(guò)查詢SQL來(lái)加鎖所以在加索前一定要分析下SQL語(yǔ)句。同時(shí)索引的命中并不是通過(guò)desc或explain就可以絕對(duì)確定的,MySQL查詢優(yōu)化處理會(huì)判斷使用索引與不使用索引的最佳方案,一般通過(guò)SQL解析中不會(huì)命令索引的就不會(huì)使用索引,會(huì)使用到索引的需要人為的再去判斷下所對(duì)應(yīng)的數(shù)據(jù)量在整表中占比數(shù)(唯一或主鍵索引一般不需要判斷),占比量越小越好(參考 < 30% 以內(nèi))并且查詢的數(shù)量越小越好(參考 < 5000)。InnoDB的行鎖在事務(wù)內(nèi)有效,釋放鎖與事務(wù)提交、回滾同時(shí)進(jìn)行,如果沒(méi)有開(kāi)啟事務(wù)加鎖則會(huì)在執(zhí)行完SQL后自動(dòng)釋放鎖(這個(gè)自動(dòng)釋放受autocommit配置影響,autocommit是自動(dòng)提交事務(wù)開(kāi)關(guān),當(dāng)打開(kāi)時(shí)所有InnoDB引擎的SQL在沒(méi)有開(kāi)啟事務(wù)時(shí)會(huì)默認(rèn)給要執(zhí)行的SQL開(kāi)啟一個(gè)事務(wù)并在執(zhí)行完后提交這個(gè)事務(wù),如果是手動(dòng)開(kāi)啟事務(wù)則這個(gè)選項(xiàng)無(wú)效)。行級(jí)鎖加鎖慢。
InnoDB索在官方文檔中已經(jīng)有說(shuō)明:https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html
總體來(lái)說(shuō)有兩種鎖形式,一個(gè)是獨(dú)占鎖、一個(gè)是共享鎖。這兩種鎖可以并存,也就是同一個(gè)會(huì)話可以給同一個(gè)查詢記錄添加這兩種鎖(需要做兩次加鎖)。
獨(dú)占鎖
用于鎖定指定記錄或表不允許其它會(huì)話連接進(jìn)程修改或加任意鎖僅允許讀。這樣可以保證數(shù)據(jù)在當(dāng)前會(huì)話中具體原始性和絕對(duì)的專享(只有釋放了獨(dú)占鎖其它會(huì)話才可以加鎖)。
通用SQL語(yǔ)法
select .... for update;
MySQL8以后升級(jí)語(yǔ)法
select .... for update [of table_name [, table_name ...]] [nowait | skip locked];
在MySQL8以后的版本可以在加鎖時(shí)增加其它選項(xiàng)。
[of table_name [, table_name ...]] 選項(xiàng)是如果不想把所有查詢的表加鎖就可以通過(guò)of來(lái)指定要加鎖的表。
[nowait | skip locked] 選項(xiàng)是指定在加鎖時(shí)發(fā)現(xiàn)數(shù)據(jù)已經(jīng)被其它會(huì)話鎖定應(yīng)該如何處理。nowait 是不再等待其它會(huì)話釋放鎖直接返回加鎖失敗錯(cuò)誤;skip locked是跳過(guò)鎖等待直接強(qiáng)制加鎖(這時(shí)鎖權(quán)限將被強(qiáng)制奪回,釋放后才會(huì)留給其它已經(jīng)加鎖的會(huì)話)。
注意:
獨(dú)占鎖最終是行鎖或表鎖由內(nèi)部引擎決定并不一定解析有索引就會(huì)是行鎖。
獨(dú)占鎖通過(guò)查詢條件添加,通過(guò)索引來(lái)確定鎖行量(或改為表鎖)。建議查詢條件中索引顆粒最小的條件放到前面,否則容易變成表鎖。
命中索引之外的查詢條件不會(huì)影響行鎖量。也就是說(shuō)如果已經(jīng)命中某個(gè)索引再添加的其它條件不會(huì)縮小鎖行量,不要以為只鎖定條件匹配的數(shù)據(jù)。
其它進(jìn)程在修改被鎖定的數(shù)據(jù)會(huì)進(jìn)入有限等待,等待時(shí)長(zhǎng)受配置 innodb_lock_wait_timeout 限制,默認(rèn)50S左右可以修改。
InnoDB獨(dú)占鎖行鎖有個(gè)區(qū)間特點(diǎn),當(dāng)插入的數(shù)據(jù)也在獨(dú)占鎖的索引范圍內(nèi),也會(huì)被鎖住,只有待鎖的釋放才可以完成插入操作。
[nowait | skip locked] 兩個(gè)選項(xiàng)使用需要謹(jǐn)慎,它們會(huì)擾亂正常的鎖流程,除非你非常確定要做什么。
同一個(gè)會(huì)話可以重復(fù)加不同索引或相關(guān)索引的獨(dú)占鎖,每次鎖定均有效。
事務(wù)內(nèi)的增、刪、改操作會(huì)自動(dòng)增加獨(dú)占鎖(這是一種隱式鎖,數(shù)據(jù)庫(kù)自動(dòng)完成的,所以只要操作事務(wù)就有可能在加鎖)。增加記錄會(huì)鎖定增加行,如果其它會(huì)話有意修改可以查詢到這條記錄會(huì)進(jìn)入鎖等待中。
常用框架已經(jīng)提供鎖操作的功能,比如:
Laravel
ModelClass::where(‘id‘, 1)->lock()->first();
ThinkPHP
ModelClass::where(‘id‘, 1)->lock(true)->first();
查看InnoDB鎖信息
show engine innodb status;
注意:鎖定的行數(shù)可以直接說(shuō)明是表鎖還是行鎖,鎖定行數(shù)接近或大于表總數(shù)說(shuō)明是表鎖,否則就是行鎖。
共享鎖
用于鎖定指定記錄或表不允許其它會(huì)話連接進(jìn)程修改或加獨(dú)占鎖僅允許加共享鎖或讀。也就是說(shuō)共享鎖只有全部釋放或只有當(dāng)前會(huì)話持有才可以進(jìn)行修改被鎖的數(shù)據(jù)。
通用SQL語(yǔ)法
select .... lock in share mode;
MySQL8以后升級(jí)語(yǔ)法
select .... for share [of table_name [, table_name ...]] [nowait | skip locked];
注意:
共享鎖只是用于在修改數(shù)據(jù)前沒(méi)有其它會(huì)話的共享鎖或獨(dú)占鎖存在才修改
MySQL8以后升級(jí)語(yǔ)法與獨(dú)占鎖加鎖功能一樣,但同時(shí)也支持通用語(yǔ)法,兩者在一個(gè)SQL里只能使用一個(gè)。
共享鎖是可重加鎖,應(yīng)用場(chǎng)景比較少。一般用于共享鎖數(shù)據(jù)只讀場(chǎng)景。
多個(gè)事務(wù)加相同的共享鎖時(shí)再修改共享鎖下的數(shù)據(jù)會(huì)造成死鎖,不過(guò)數(shù)據(jù)庫(kù)處理自動(dòng)結(jié)束死鎖進(jìn)程。
常用框架已經(jīng)提供鎖操作的功能,比如:
Laravel
ModelClass::where(‘id‘, 1)->sharedLock()->first();
ThinkPHP6
ModelClass::where(‘id‘, 1)->lock(‘lock in share mode‘)->first();
使用鎖
鎖的概念很多,使用時(shí)不需要全部清楚,但使用時(shí)需要注意或規(guī)避一些問(wèn)題。
業(yè)務(wù)邏輯中多個(gè)加鎖順序保持一至性,一張表盡量減少多次加鎖,避免出現(xiàn)業(yè)務(wù)死鎖。
事務(wù)內(nèi)的增、刪、改都會(huì)產(chǎn)生鎖,需要避免操作的一至性,或提前獲取鎖。
數(shù)據(jù)庫(kù)拋出的死鎖會(huì)導(dǎo)致事務(wù)回滾,業(yè)務(wù)代碼中盡量使用一個(gè)事務(wù)完成操作。
查詢加鎖時(shí)一定得保證鎖的意義,同時(shí)也需要減少鎖的數(shù)據(jù)量,盡量使用唯一索引查詢加鎖,過(guò)度使用鎖會(huì)嚴(yán)重影響系統(tǒng)性能。
事務(wù)隔離等級(jí)影響
事務(wù)隔離等級(jí)對(duì)鎖產(chǎn)生影響不大,事務(wù)隔離只是為了解決事務(wù)處理中的讀臟問(wèn)題。MySQL有4種等級(jí)的事務(wù)隔離分別是:
可讀未提交(read uncommitted)。各事務(wù)之間可以讀取未提交的數(shù)據(jù),如果事務(wù)回滾就會(huì)導(dǎo)致其它事務(wù)讀取的是臟數(shù)據(jù),這種隔離等級(jí)是最低的,一般業(yè)務(wù)不會(huì)采用。
可讀已提交(read committed)。事務(wù)處理中可以讀取已經(jīng)提交的數(shù)據(jù),這種隔離已經(jīng)解決讀臟的問(wèn)題,事務(wù)處理中不保證前后讀取的數(shù)據(jù)一至性,因?yàn)榇嬖谄渌聞?wù)修改提交后再查詢就有變化。如果同一個(gè)事務(wù)內(nèi)多次讀取相同的數(shù)據(jù)可能會(huì)出現(xiàn)不一樣的結(jié)果,那之前讀數(shù)據(jù)可能是幻讀(因?yàn)閿?shù)據(jù)已經(jīng)被修改)。大部分情況下,這種事務(wù)隔離已經(jīng)滿足業(yè)務(wù)需求。
可重讀(repeatable read)。事務(wù)處理中讀取相關(guān)的數(shù)據(jù)會(huì)有相關(guān)的結(jié)果,不會(huì)受其它事務(wù)的提交修改而影響。如果同一個(gè)事務(wù)內(nèi)多次讀取相同的數(shù)據(jù)不會(huì)出現(xiàn)不一樣的結(jié)果,那之后讀取的數(shù)據(jù)可能是幻讀(因?yàn)閿?shù)據(jù)已經(jīng)被修改)。這是MySQL默認(rèn)的事務(wù)隔離等級(jí)。
串行(serializable)。這種事務(wù)隔離等級(jí)相當(dāng)于在可重讀等級(jí)下給每個(gè)事務(wù)內(nèi)的查詢SQL后增加for update來(lái)添加獨(dú)占鎖,保證事務(wù)內(nèi)其它事務(wù)不可修改,從而解決了事務(wù)內(nèi)的幻讀問(wèn)題。這種事務(wù)隔離等級(jí)最高,也最理想,但性能最差,一般業(yè)務(wù)不會(huì)采用。
事務(wù)隔離等級(jí)會(huì)直接影響數(shù)據(jù)庫(kù)的事務(wù)處理能力,MySQL這4種事務(wù)等級(jí)提供了更多的選擇,等級(jí)越低事務(wù)處理能力越強(qiáng)。事務(wù)的理想狀態(tài)是事務(wù)處理中所有需要的數(shù)據(jù)都保留了原始狀態(tài)下進(jìn)行操作處理,但實(shí)際應(yīng)用中需要做一些取舍,好在數(shù)據(jù)庫(kù)已經(jīng)默認(rèn)為我們選擇了更好的事務(wù)隔離等級(jí),如果沒(méi)有特別的要求一般不建議修改事務(wù)隔離等級(jí)。
查詢當(dāng)前事務(wù)等級(jí)
show variables like ‘%isolation‘;
設(shè)置事務(wù)等級(jí)
set [global | session] transaction isolation level [read uncommitted | read committed | repeatable read | serializable]
注意:global 是修改當(dāng)前會(huì)話之后的后續(xù)所有會(huì)話事務(wù)(如果是工具可能需要重新連接數(shù)據(jù)才有效果),session 是當(dāng)前會(huì)話下后續(xù)所有事務(wù),不影響其它會(huì)話。
配置文件修改事務(wù)等級(jí)
[mysqld]
transaction-isolation = REPEATABLE-READ
transaction-read-only = OFF
官方文檔:https://dev.mysql.com/doc/refman/8.0/en/set-transaction.html
Mysql鎖一般使用
標(biāo)簽:多個(gè)???會(huì)話???概念???加鎖???指定表???最小???縮小???決定???tin
本條技術(shù)文章來(lái)源于互聯(lián)網(wǎng),如果無(wú)意侵犯您的權(quán)益請(qǐng)點(diǎn)擊此處反饋版權(quán)投訴
本文系統(tǒng)來(lái)源:https://blog.51cto.com/php2012web/2500661
總結(jié)
以上是生活随笔為你收集整理的mysql 锁怎么使用_Mysql锁一般使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Spring对java模块化支持_Spr
- 下一篇: oracle rman恢复表空间,Ora