Mysql鎖之共享鎖(讀鎖)和排他鎖(寫鎖)詳解
InnoDB和MyISAM
Mysql在5.5之前默認使用MyISAM存儲引擎,之后使用InnoDB。
查看當前存儲引擎:
show variables like ‘%storage_engine%';
MyISAM操作數(shù)據(jù)都是使用的表鎖,你更新一條記錄就要鎖整個表,導致性能較低,并發(fā)不高。當然它也不會存在死鎖問題。
InnoDB與M有ISAM的最大不同有兩點
- InnoDB支持事務
- InnoDB采用了行級鎖。也就是你需要修改哪行,就可以只鎖定哪行。
在Mysql中,行級鎖并不是直接鎖記錄,而是鎖索引。
索引分為主鍵索引和非主鍵索引兩種,如果一條sql語句操作了主鍵索引,Mysql就會鎖定這條主鍵索引;如果一條語句操作了非主鍵索引,Mysql會先鎖定該非主鍵索引,再鎖定相關(guān)的主鍵索引。
InnoDB行鎖是通過給索引加鎖實現(xiàn)的,如果沒有索引,InnoDB會通過因此的聚簇索引來對記錄加鎖。
也就是說:如果不通過索引條件檢索數(shù)據(jù),那么InnoDB將對表中所有數(shù)據(jù)加鎖,實際效果跟表鎖一樣。因為沒有了索引,找到記錄就得掃描全表,要掃描全表,就得鎖定表。
共享鎖與排他鎖
首先說明:InnoDB引擎默認對update,delete,insert加排它鎖,select語句默認不加鎖
共享鎖
共享鎖shared locks(S鎖)也稱讀鎖:用于不更改或不更新數(shù)據(jù)的操作(只讀操作),可以查看但無法修改和刪除的一種數(shù)據(jù)鎖,如select語句。
如果事務T對數(shù)據(jù)A加上共享鎖后,則其他事務只能對數(shù)據(jù)A再加共享鎖,不能加排他鎖。獲準共享鎖的事務只能讀數(shù)據(jù),不能修改數(shù)據(jù)。共享鎖下其它用戶可以并發(fā)讀取,查詢數(shù)據(jù)。但不能修改,增加,刪除數(shù)據(jù)。資源共享。
加鎖方式
select ... lock in share mode
注意:
- 對于使用共享鎖的事務,其他事務只能讀,不可寫
- 如果執(zhí)行了更新操作則會一直等待,直到當前事務commit或者rollback
- 如果當前事務也執(zhí)行了其他事務處于等待的那條sql語句,當前事務將會執(zhí)行成功,而其他事務會報死鎖
- 允許其他鎖共存
排它鎖
排它鎖Exclusive Locks(X鎖)也稱寫鎖、獨占鎖:用于數(shù)據(jù)修改操作,例如insert、update或delete。確保不會同時對同一資源進行多重更新。
如果事務T對數(shù)據(jù)A加上排它鎖后,則其他事務不能在對A加任何類型的鎖。獲準排他鎖的事務既能讀數(shù)據(jù),又能修改數(shù)據(jù)。我們在操作數(shù)據(jù)庫的時候,可能會由于并發(fā)問題而引起的數(shù)據(jù)的不一致性(數(shù)據(jù)沖突)
加鎖方式
select ... for update
**for update:**InnoDB默認是行級別的鎖,當有明確指定的主鍵是,使用的是行鎖;否則使用表鎖。使用情況詳細如下:
明確指定主鍵,并且存在此記錄,行級鎖。例如:
-- id是主鍵 select name,age from table_user where id = '1' for update;
明確指定主鍵,若查無記錄,無鎖。例如:
-- id是主鍵,單不存在id = 1的數(shù)據(jù) select name,age from table_user where id = '1' for update;
無主鍵,表級鎖。例如:
-- age是普通字段 select name,age from table_user where age = 12 for update;
主鍵不明確,表級鎖。例如:
-- id是主鍵,age不是,但數(shù)據(jù)庫 select name,age from table_user where age = 12 and id = '1' for update;
注意:
- 對于排它鎖的事務,其它事務可讀,但不可進行更新操作
- for update僅使用與InnoDB,并且必須開啟事務,在begin和commit之間才生效
- 當一個事務進行for update的時候,另一個事務也有for update時會一直等待,直到之前的事務commit或rollback或斷開連接釋放鎖才能拿到鎖進行后面的操作(排它鎖不能共存)
- InnoDB引擎默認對update,delete,insert加排它鎖,select語句默認不加鎖
- 加過排他鎖的數(shù)據(jù)行在其他事務中是不能修改數(shù)據(jù)的,也不能通過for update和lock in share mode鎖的方式查詢數(shù)據(jù),但可以直接通過select … from …查詢數(shù)據(jù),因為普通查詢沒有任何鎖機制
樂觀鎖與悲觀鎖
首先說明:樂觀鎖和悲觀鎖都是針對讀(select)來說的。
樂觀鎖
樂觀鎖不是數(shù)據(jù)庫自帶的,需要我們自己去實現(xiàn)。
樂觀鎖是指操作數(shù)據(jù)庫時(更新操作),想法很樂觀,認為這次的操作不會導致沖突,在操作數(shù)據(jù)時,并不進行任何其他的特殊處理(也就是不加鎖),而在進行更新后,再去判斷是否有沖突了。
悲觀鎖
悲觀鎖就是在操作數(shù)據(jù)時,認為此操作會出現(xiàn)數(shù)據(jù)沖突,所以在進行每次操作時都要通過獲取鎖才能進行對相同數(shù)據(jù)的操作,這點跟Java中的synchronized很相似,所以悲觀鎖需要耗費較多的時間。
另外與樂觀鎖相對應,悲觀鎖是由數(shù)據(jù)庫自己實現(xiàn)了的,要用的時候,我們直接調(diào)用數(shù)據(jù)庫的相關(guān)語句就可以了。
由悲觀鎖涉及到的另外兩個鎖概念,就是共享鎖與排他鎖。共享鎖和排他鎖是悲觀鎖的不同實現(xiàn),它倆都屬于悲觀鎖的范疇。
案例
某商品,用戶購買后庫存應-1,而某兩個或多個用戶同時購買,此時三個執(zhí)行程序均同時讀得庫存為“n”,之后進行了一些操作,最后將均執(zhí)行update table set 庫存書 = n - 1,那么,很顯然這是錯誤的。
解決
1.使用悲觀鎖(也就是排他鎖)
- 程序A在查詢庫存數(shù)時使用排他鎖(select * from table where id = 10 for update)
- 然后進行后續(xù)操作,包括更新庫存數(shù),最后提交事務
- 程序B在查詢庫存數(shù)時,如果A還未釋放排他鎖,程序B將等待。。。
- 程序C同B。。。
2.使用樂觀鎖(靠表設計和代碼來實現(xiàn))
一般是在該商品表添加version版本字段或者timestamp時間戳字段
程序A查詢后,執(zhí)行更新變成了:
update table set num = num - 1 and version = 23
這樣,保證了修改的數(shù)據(jù)是和它查詢出來的數(shù)據(jù)是一致的(其他執(zhí)行程序肯定未進行修改)。當然,如果更新失敗,表示在更新操作之前,有其他執(zhí)行程序已經(jīng)更新了該庫存數(shù),那么就可以嘗試重試來保證更新成功。為了盡可能避免更新失敗,可以合理調(diào)整重試次數(shù)(阿里巴巴開發(fā)手冊規(guī)定重試次數(shù)不低于三次)。
樂觀鎖和悲觀鎖的區(qū)別
悲觀鎖實際使用了排他鎖來實現(xiàn)**(select … for update)**。InnoDB加行鎖的前提是:必須是通過索引條件來檢索數(shù)據(jù),否則會切換為表鎖。
因此,悲觀鎖在未通過索引條件檢索數(shù)據(jù)時,會鎖定整張表。導致其他程序不允許**“加鎖的查詢操作”**,影響吞吐。因此,如果在查詢居多的情況下,推薦使用樂觀鎖。
加鎖的查詢操作:加過排他鎖的數(shù)據(jù)行在其他事務中是不能修改的,也不能通過for update或lock in share mode的加鎖方式查詢,但可以直接通過select … from …查詢數(shù)據(jù),因為普通查詢沒有任何鎖機制。
樂觀鎖更新有可能會失敗,甚至是更新幾次都失敗,這是有風險的。所以如果寫入居多,對吞吐要求不高,可使用悲觀鎖。
結(jié)尾:讀用樂觀鎖,寫用悲觀鎖。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
MySQL數(shù)據(jù)庫定時備份的幾種實現(xiàn)方法
本文主要介紹了MySQL數(shù)據(jù)庫定時備份的幾種實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2024-07-07遠程連接mysql報錯“Host?xxx?is?not?allowed?to?connect?to?th
這篇文章主要給大家介紹了關(guān)于遠程連接mysql報錯“Host?xxx?is?not?allowed?to?connect?to?this?MySQL?server“的解決辦法,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-07-07MySQL字段類型與Java實體類類型對應轉(zhuǎn)換關(guān)系詳解
這篇文章主要介紹了MySQL字段類型與Java實體類類型對應轉(zhuǎn)換關(guān)系,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06