數據庫鎖知識mysql
很多人在開發的時候,應該不多會注意到這些鎖的問題,也不多會給程序加鎖(除了庫存這些對數量準確性要求極高的狀況下),即便咱們不會這些鎖知識,咱們的程序在通常狀況下仍是能夠跑得好好的。由於這些鎖數據庫隱式幫咱們加了,只會在某些特定的場景下才須要手動加鎖。程序員
對於UPDATE、DELETE、INSERT語句,InnoDB會自動給涉及數據集加排他鎖(X) MyISAM在執行查詢語句SELECT前,會自動給涉及的全部表加讀鎖,在執行增、刪、改操做前,會自動給涉及的表加寫鎖,這個過程並不須要用戶干預sql
首先,從鎖的粒度,咱們能夠分紅兩大類:
表鎖 :開銷小,加鎖快;不會出現死鎖;鎖定力度大,發生鎖衝突機率高,併發度最低
行鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度小,發生鎖衝突的機率低,併發度高 不一樣的存儲引擎支持的鎖粒度是不同的==:InnoDB行鎖和表鎖都支持、MyISAM只支持表鎖!InnoDB只有經過索引條件檢索數據才使用行級鎖==,不然,InnoDB使用表鎖也就是說,InnoDB的行鎖是基於索引的!數據庫
表鎖下又分爲兩種模式: 表讀鎖(Table Read Lock)&& 表寫鎖(Table Write Lock)
從下圖能夠清晰看到,在表讀鎖和表寫鎖的環境下:讀讀不阻塞,讀寫阻塞,寫寫阻塞!讀讀不阻塞:當前用戶在讀數據,其餘的用戶也在讀數據,不會加鎖 讀寫阻塞:當前用戶在讀數據,其餘的用戶不能修改當前用戶讀的數據,會加鎖!寫寫阻塞:當前用戶在修改數據,其餘的用戶不能修改當前用戶正在修改的數據,會加鎖!併發
從上面已經看到了:讀鎖和寫鎖是互斥的,讀寫操做是串行。性能
注:spa
行鎖 code
InnoDB和MyISAM有兩個本質的區別:InnoDB支持行鎖、InnoDB支持事務blog
InnoDB實現瞭如下兩種類型的行鎖:索引
另外,爲了容許行鎖和表鎖共存,實現多粒度鎖機制,InnoDB還有兩種內部使用的意向鎖(Intention Locks),這兩種意向鎖都是表鎖:
MVCC(Multi-Version ConcurrencyControl)多版本併發控制,能夠簡單地認爲:MVCC就是行級鎖的一個變種(升級版)。在表鎖中咱們讀寫是阻塞的,基於提高併發性能的考慮,MVCC通常讀寫是不阻塞的(不少狀況下避免了加鎖的操做)。
能夠簡單的理解爲:對數據庫的任何修改的提交都不會直接覆蓋以前的數據,而是產生一個新的版本與老版本共存,使得讀取時能夠徹底不加鎖。
事務的隔離級別就是經過鎖的機制來實現,鎖的應用最終致使不一樣事務的隔離級別,只不過隱藏了加鎖細節,事務的隔離級別有4種:
Read uncommitted:出現的現象--->髒讀:一個事務讀取到另一個事務未提交的數據,例子:A向B轉帳,A執行了轉帳語句,但A尚未提交事務,B讀取數據,發現本身帳戶錢變多了!B跟A說,我已經收到錢了。A回滾事務【rollback】,等B再查看帳戶的錢時,發現錢並無多...
Read committed:出現的現象--->不可重複讀:一個事務讀取到另一個事務已經提交的數據,也就是說一個事務能夠看到其餘事務所作的修改,例如:A查詢數據庫獲得數據,B去修改數據庫的數據,致使A屢次查詢數據庫的結果都不同【危害:A每次查詢的結果都是受B的影響的,那麼A查詢出來的信息就沒有意思了】
Repeatable read:避免不可重複讀是事務級別的快照!每次讀取的都是當前事務的版本,即便被修改了,也只會讀取當前事務版本的數據
至於虛讀(幻讀):是指在一個事務內讀取到了別的事務插入的數據,致使先後讀取不一致。和不可重複讀相似,但虛讀(幻讀)會讀到其餘事務的插入的數據,致使先後讀取不 一致,幻讀的重點在於新增或者刪除(數據條數變化),不可重複讀的重點是修改,幻讀和不可重複的區別?
不管是Read committed仍是Repeatable read隔離級別,都是爲了解決讀寫衝突的問題,如今考慮一個問題:有一張數據庫表USER,只有id、name字段,如今有2個請求同時操做表A,過程以下:(模擬更新丟失,雖然不是很恰當)
1. 操做1查詢出name="zhangsan"
2. 操做2也查詢出name="zhangsan"
3. 操做1把name字段數據修改爲lisi並提交
4. 操做2把name字段數據修改成wangwu並提交
那麼操做1的更新丟失啦,即一個事務的更新覆蓋了其它事務的更新結果,解決上述更新丟失的方式有以下3種:
悲觀鎖
咱們使用悲觀鎖的話其實很簡單(手動加行鎖就好了):select * from xxxx for update,在select 語句後邊加了for update至關於加了排它鎖(寫鎖),加了寫鎖之後,其餘事務就不能對它修改了!須要等待當前事務修改完以後才能夠修改.也就是說,若是操做1使用select ... for update,操做2就沒法對該條記錄修改了,便可避免更新丟失。
樂觀鎖
樂觀鎖不是數據庫層面上的鎖,須要用戶手動去加的鎖。通常咱們在數據庫表中添加一個版本字段version來實現,例如操做1和操做2在更新User表的時,執行語句以下:
update A set Name=lisi,version=version+1 where ID=#{id} and version=#{version},
此時便可避免更新丟失。
當咱們用範圍條件檢索數據而不是相等條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合範圍條件的已有數據記錄的索引項加鎖;對於鍵值在條件範圍內但並不存在 的記錄,叫作「間隙(GAP)」。InnoDB也會對這個「間隙」加鎖,這種鎖機制就是所謂的間隙鎖。例子:假如emp表中只有101條記錄,其empid的值分別是1,2,...,100,101
Select * from emp where empid > 100 for update;
上面是一個範圍查詢,InnoDB不只會對符合條件的empid值爲101的記錄加鎖,也會對empid大於101(這些記錄並不存在)的「間隙」加鎖
InnoDB使用間隙鎖的目的有2個:
併發的問題就少不了死鎖,在MySQL中一樣會存在死鎖的問題
表鎖其實咱們程序員是不多關心它的:
如今咱們大多數使用MySQL都是使用InnoDB,InnoDB支持行鎖:
在默認的狀況下,select是不加任何行鎖的~事務能夠經過如下語句顯示給記錄集加共享鎖或排他鎖。
InnoDB基於行鎖還實現了MVCC多版本併發控制,MVCC在隔離級別下的Read committed和Repeatable read下工做。MVCC實現了讀寫不阻塞