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