for update 的作用是在查詢的時(shí)候?yàn)樾屑由吓潘i,當(dāng)一個(gè)事務(wù)的操作未完成時(shí)候,其他事務(wù)可以讀取但是不能寫(xiě)入或更新。
它的典型使用場(chǎng)景是高并發(fā)并且對(duì)于數(shù)據(jù)的準(zhǔn)確性有很高要求,比如金錢(qián)、庫(kù)存等,一般這種操作都是很長(zhǎng)一串并且開(kāi)啟事務(wù)的,假如現(xiàn)在要對(duì)庫(kù)存進(jìn)行操作,在剛開(kāi)始讀的時(shí)候是1,然后馬上另外一個(gè)進(jìn)程將庫(kù)存更新為0了,但事務(wù)還沒(méi)結(jié)束,會(huì)一直用1進(jìn)行后續(xù)的邏輯,就會(huì)有問(wèn)題,所以需要用for upate 加鎖防止出錯(cuò)。
InnoDB行鎖類(lèi)型
行鎖的具體實(shí)現(xiàn)算法有三種:record lock、gap lock以及next-key lock。
- record lock是專門(mén)對(duì)索引項(xiàng)加鎖;
- gap lock是對(duì)索引項(xiàng)之間間隙加鎖;
- next-lock則是前面兩種的組合,對(duì)索引項(xiàng)以及之間的間隙加鎖。
只在可重復(fù)讀或以上隔離級(jí)別下的特定操作才會(huì)取得 gap lock 或 next-key lock,在 Select、Update 和 Delete 時(shí),除了基于唯一索引的查詢之外,其它索引查詢時(shí)都會(huì)獲取 gap lock 或 next-key lock,即鎖住其掃描的范圍。主鍵索引也屬于唯一索引,所以主鍵索引是不會(huì)使用 gap lock 或 next-key lock
for update 的使用方式
for update 僅適用于InnoDB,并且必須開(kāi)啟事務(wù),在begin與commit之間才生效。
select * from table_name where ... for update
select 語(yǔ)句默認(rèn)不獲取任何鎖,所以是可以讀被其它事務(wù)持有排它鎖的數(shù)據(jù)的!
實(shí)踐
InnoDB 既實(shí)現(xiàn)了行鎖,也實(shí)現(xiàn)了表鎖。
當(dāng)有明確指定的主鍵/索引時(shí)候,是行級(jí)鎖,否則是表級(jí)鎖
假設(shè)表 user,存在有id跟name字段,id是主鍵,有5條數(shù)據(jù)。
1. 行級(jí)鎖
明確指定主鍵,并且有此記錄,行級(jí)鎖
SELECT * FROM user WHERE id = 1 FOR UPDATE;
SELECT * FROM user WHERE id = 1 and name = 'segon' FOR UPDATE;
| 進(jìn)程1 | 進(jìn)程2 |
|---|---|
| begin; | |
| SELECT * FROM user WHERE id = 1 FOR UPDATE; | -- |
| -- | UPDATE user SET name = ‘test’ WHERE id = 2; – 成功 |
| -- | UPDATE user SET name = ‘test’ WHERE id = 1; – 等待 |
| commit; | -- |
| -- | 執(zhí)行等待的任務(wù),成功 |
2. 表級(jí)鎖
無(wú)主鍵/索引,表級(jí)鎖
SELECT * FROM user WHERE name = 'segon' FOR UPDATE;
主鍵/索引不明確,表級(jí)鎖
SELECT * FROM user WHERE id <> 3 FOR UPDATE;
SELECT * FROM user WHERE id LIKE '3' FOR UPDATE;
| 進(jìn)程1 | 進(jìn)程2 |
|---|---|
| begin; | |
| SELECT * FROM user WHERE id LIKE ‘3’ FOR UPDATE; | -- |
| -- | UPDATE user SET name = ‘test’ WHERE id = 1; – 等待 |
| commit; | -- |
| -- | 執(zhí)行等待的任務(wù),成功 |
3. 無(wú)鎖
明確指定主鍵/索引,若查無(wú)此記錄,無(wú)鎖
SELECT * FROM user WHERE id = -1 FOR UPDATE;
| 進(jìn)程1 | 進(jìn)程2 |
|---|---|
| begin; | |
| SELECT * FROM user WHERE id = -1 FOR UPDATE; | |
| -- | UPDATE user SET name = ‘test’ WHERE id = 2; – 成功 |
| commit; |