1. 程式人生 > 實用技巧 >mysql使用鎖的分析

mysql使用鎖的分析

MySQL中的共享鎖與排他鎖

1,共享鎖與排他鎖
1.首先說明:資料庫的增刪改操作預設都會加排他鎖,而查詢不會加任何鎖。
|--共享鎖:對某一資源加共享鎖,自身可以讀該資源,其他人也可以讀該資源(也可以再繼續加共享鎖,即 共享鎖可多個共存),但無法修改。要想修改就必須等所有共享鎖都釋放完之後。語法為:
select * from table lock in share mode
|--排他鎖:對某一資源加排他鎖,自身可以進行增刪改查,其他人無法進行任何操作。語法為:
select * from table for update --增刪改自動加了排他鎖

共享鎖(Share Lock)

在查詢語句後面增加LOCK IN SHARE MODE

,Mysql會對查詢結果中的每行都加共享鎖,當沒有其他執行緒對查詢結果集中的任何一行使用排他鎖時,可以成功申請共享鎖,否則會被阻塞。其他執行緒也可以讀取使用了共享鎖的表,而且這些執行緒讀取的是同一個版本的資料。

例1:-------------------------------------------------------------------------------------------------------------------------------------
T1:select * from table lock in share mode(假設查詢會花很長時間,下面的例子也都這麼假設)
T2:update table set column1='hello'
T1代表一個數據庫執行請求,T2代表另一個請求,也可以理解為T1為一個執行緒,T2 為另一個執行緒。
過程:
T1執行(並加共享鎖)
T2執行
If T1還沒執行完
T2等......
else鎖被釋放
T2執行
endif

T2 之所以要等,是因為 T2 在執行 update 前,試圖對 table 表加一個排他鎖,而資料庫規定同一資源上不能同時共存共享鎖和排他鎖。所以 T2 必須等 T1 執行完,釋放了共享鎖,才能加上排他鎖,然後才能開始執行 update 語句。

例2:-------------------------------------------------------------------------------------------------------------------------------------
T1:select * from table lock in share mode
T2:select * from table lock in share mode

這裡T2不用等待T1執行完,而是可以馬上執行。

分析:
T1執行,則 table 被加鎖,比如叫lockA T2執行,再對 table 加一個共享鎖,比如叫lockB兩個鎖是可以同時存在於同一資源上的(比如同一個表上)。這被稱為共享鎖與共享鎖相容。這意味著共享鎖不阻止其它人同時讀資源,但阻止其它人修改資源。

例3:-------------------------------------------------------------------------------------------------------------------------------------
T1:select * from table lock in share mode
T2:select * from table lock in share mode
T3:update table set column1='hello'

T2 不用等 T1 執行完就能執行,T3 卻要等 T1 和 T2 都執行完才能執行。因為 T3 必須等 T1 和 T2 的共享鎖全部釋放才能進行加排他鎖然後執行 update 操作。

排他鎖(eXclusive Lock)

排他鎖又稱寫鎖,如果事務T對資料A加上排他鎖後,則其他事務不能再對A加任任何型別的封鎖。獲准排他鎖的事務既能讀資料,又能修改資料。

用法

SELECT ... FOR UPDATE;

在查詢語句後面增加FOR UPDATE,Mysql會對查詢結果中的每行都加排他鎖,當沒有其他執行緒對查詢結果集中的任何一行使用排他鎖時,可以成功申請排他鎖,否則會被阻塞。

意向鎖

InnoDB還有兩個表鎖:

意向共享鎖(IS):表示事務準備給資料行加入共享鎖,也就是說一個數據行加共享鎖前必須先取得該表的IS鎖

意向排他鎖(IX):類似上面,表示事務準備給資料行加入排他鎖,說明事務在一個數據行加排他鎖前必須先取得該表的IX鎖。

意向鎖是InnoDB自動加的,不需要使用者干預。

對於insert、update、delete,InnoDB會自動給涉及的資料加排他鎖(X);對於一般的Select語句,InnoDB不會加任何鎖,事務可以通過以下語句給顯示加共享鎖或排他鎖。

共享鎖:SELECT ... LOCK IN SHARE MODE;

排他鎖:SELECT ... FOR UPDATE;

樂觀鎖與悲觀鎖
案例:
某商品,使用者購買後庫存數應-1,而某兩個或多個使用者同時購買,此時三個執行程式均同時讀得庫存為n,之後進行了一些操作,最後將均執行update table set 庫存數=n-1,那麼,很顯然這是錯誤的。

解決:
1.使用悲觀鎖(其實說白了也就是排他鎖)
|--程式A在查詢庫存數時使用排他鎖(select * from table where id=10 for update)
|--然後進行後續的操作,包括更新庫存數,最後提交事務。
|--程式B在查詢庫存數時,如果A還未釋放排他鎖,它將等待。
|--程式C同B……
2.使用樂觀鎖(靠表設計和程式碼來實現)
|--一般是在該商品表新增version版本欄位或者timestamp時間戳欄位
|--程式A查詢後,執行更新變成了:
update table set num=num-1 where id=10 and version=23
這樣,保證了修改的資料是和它查詢出來的資料是一致的,而其他執行程式未進行修改。當然,如果更新失敗,表示在更新操作之前,有其他執行程式已經更新了該庫存數,那麼就可以嘗試重試來保證更新成功。為了儘可能避免更新失敗,可以合理調整重試次
數(阿里巴巴開發手冊規定重試次數不低於三次)。 總結:對於以上,可以看得出來樂觀鎖和悲觀鎖的區別。 1.悲觀鎖使用了排他鎖,當程式獨佔鎖時,其他程式就連查詢都是不允許的,導致吞吐較低。如果在查詢較多的情況下,可使用樂觀鎖。 2.樂觀鎖更新有可能會失敗,甚至是更新幾次都失敗,這是有風險的。所以如果寫入較頻繁,對吞吐要求不高,可使用悲觀鎖。 也就是一句話:讀用樂觀鎖,寫用悲觀鎖。