MySQL的select for update用法


MySQL中的select for update大家應該都有所接觸,但什么時候該去使用,以及有哪些需要注意的地方會有很多不清楚的地方,我把我如何使用和查詢到的文檔在此記錄。

作用

select本身是一個查詢語句,查詢語句是不會產生沖突的一種行為,一般情況下是沒有鎖的,用select for update 會讓select語句產生一個排它鎖(X), 這個鎖和update的效果一樣,會使兩個事務無法同時更新一條記錄。

什么時候使用

我認為的錯誤的用法

在網上看到一篇文章, 里面講到用select for update來產生一個悲觀鎖,保證庫存的一致性,其他update在更新的時候本身就有悲觀鎖,不會讓另外一個事務更新,如果按照他的寫法update goods set stock = stock - 1 where id = 1;是沒必要先去查詢的

我認為的正確的用法(1)

有時我們確實需要先將數據查詢出來,然后再去更新,但我們不想的是,我剛查詢完結果沒還更新,這時被其他事務更新了,例如上面的例子,如果我想先查詢庫存,確認庫存是否足夠,如果足夠就去更新,不夠就返回一個錯誤

begin;
select * from goods where id = 1;

// 檢查庫存足夠代碼

update goods set stock = stock - 1 where id = 1;
commit;

如果不用for update的情況下,在檢查庫存時,被其他事務更新了庫存,就會產生庫存不足,但是當前事務以為充足的情況發生, 所以使用for update先鎖住這條記錄,其他事務不能去更改這條記錄直到當前事務提交

我認為的正確的用法(2)

在我們實際業務中,一個訂單要扣除多個補劑的庫存,這時要用事務保證一個訂單多個補劑同時扣除成功或者同時失敗,可能會出現這樣的情況,M訂單需要補劑a,b,c三種補劑,N訂單需要c,e,a三種補劑,這時兩個事務同時執行,M事務扣除ab庫存,N事務庫存c,e庫存,因為update會產生排它鎖阻止其他事務更新當前事務已經更新的記錄,所以這會產生死鎖,M在等待獲取c的鎖,N在等待獲取a的鎖,在訂單比較多的情況下,這種沖突很容易出現,我的解決方法是當M需要a,b,c三種補劑時,使用select for update鎖定三條記錄,此時N訂單select for update獲取不到c的鎖直到M訂單事務提交

2022.03.29 Update: 經過很長一段時間工作之后,我發現我認為的正確用法(2)是錯誤的,這個和select for update沒有關系,關鍵是要對共享資源進行排序,例如:有a-z共26中補劑,M訂單需要a、d、e,N訂單需要d、h、z,這這種情況中,誰先拿到沖突的資源誰會先執行完,因為沖突的資源后面的資源肯定不是沖突的,也就是說只使用事務+多個update也可以實現

至於其他的用法可以我后面遇到會再總結進去

注意事項

  • select for update會根據where條件來鎖多條記錄, 根據where條件的字段是不是索引來決定鎖定表還是鎖定行,同時根據是聚簇索引還是二級索引和查詢條件(范圍查詢還是精確查詢)來決定鎖定特定的行還是一個范圍內的行
  • 當鎖定范圍的行時會有gap lock和next-key來防止插入新的行參考StackOverFlow
  • SELECT * FROM information_schema.innodb_trx \G 使用該語句可以查詢某個事務鎖了幾個表,鎖了幾個行

參考

[1] MySQL InnoDB存儲引擎(三):鎖及事務模型

[2] SELECT FOR UPDATE作用字段提問


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM