mysql 使用select ... for update 做事務寫入前的確認
以mysql 的innodb 為例,預設的tansaction isolation level 為repeatable read,在select 的讀取鎖定主要分為兩種方式:
select ... lock in share modeselect ... for update
這兩種方式在事務(transaction) 進行當中select 到同乙個資料表時,都必須等待其它事務資料被提交(commit)後才會執行。而主要的不同在於lock in share mode 在有一方事務要update 同乙個表單時很容易造成死鎖 。
簡單的說,如果select 後面若要update 同乙個表單,最好使用select ... update。
舉個例子: 假設商品表單products 內有乙個存放商品數量的quantity ,在訂單成立之前必須先確定quantity 商品數量是否足夠(quantity>0) ,然後才把數量更新為1。
不安全的做法:
select quantity from products where id=3;update products set quantity = 1 where id=3;
為什麼不安全呢?
少量的狀況下或許不會有問題,但是大量的資料訪問「鐵定」會出問題。
如果我們需要在quantity>0 的情況下才能扣庫存,假設程式在第一行select 讀到的quantity 是2 ,看起來數字沒有錯,但是當mysql 正準備要update 的時候,可能已經有人把庫存扣成0 了,但是程式卻渾然不知,將錯就錯的update 下去了。
因此必須透過的事務機制來確保讀取及提交的資料都是正確的。
於是我們在mysql 就可以這樣測試: (注1)
set autocommit=0;begin work;select quantity from products where id=3 for update;****************************************===此時products 資料中id=3 的資料被鎖住(注3),其它事務必須等待此次事務提交後才能執行select * from products where id=3 for update (注2)如此可以確保quantity 在別的事務讀到的數字是正確的。****************************************===update products set quantity = '1' where id=3 ;commit work;****************************************===提交(commit)寫入資料庫,products 解鎖。
注1: begin/commit 為事務的起始及結束點,可使用二個以上的mysql command 視窗來互動觀察鎖定的狀況。
注2: 在事務進行當中,只有select ... for update 或lock in share mode 同一筆資料時會等待其它事務結束後才執行,一般select ... 則不受此影響。
注3: 由於innodb 預設為row-level lock,資料列的鎖定可參考這篇。
注4: innodb 表單盡量不要使用lock tables 指令,若情非得已要使用,請先看官方對於innodb 使用lock tables 的說明,以免造成系統經常發生死鎖。
mysql select ... for update 的row lock 與table lock
上面介紹過select ... for update 的用法,不過鎖定(lock)的資料是判別就得要注意一下了。由於innodb 預設是row-level lock,所以只有「明確」的指定主鍵,mysql 才會執行row lock (只鎖住被選取的資料) ,否則mysql 將會執行table lock (將整個資料表單給鎖住)。
舉個例子:
假設有個表單products ,裡面有id 跟name 二個字段,id 是主鍵。
例1: (明確指定主鍵,並且有此資料,row lock)
select * from products where id='3' for update;
例2: (明確指定主鍵,若查無此資料,無lock)
select * from products where id='-1' for update;
例2: (無主鍵,table lock)
select * from products where name='mouse' for update;
例3: (主鍵不明確,table lock)
select * from products where id<>'3' for update;
例4: (主鍵不明確,table lock)
select * from products where id like '3' for update;
注1: for update 僅適用於innodb,且必須在事務區塊(begin/commit)中才能生效。
注2: 要測試鎖定的狀況,可以利用mysql 的command mode ,開二個視窗來做測試。
以上內容來自:
mysql事物鎖鎖表 mysql 事務 行鎖 表鎖
一 準備 select from information schema.innodb trx 查詢事務 select from information schema.innodb locks 查詢鎖 select from information schema.innodb lock waits 暫...
mysql行鎖表怎麼辦 mysql事務 表鎖 行鎖
mysql 使用select for update 做事務寫入前的確認 以mysql 的innodb 為例,預設的tansaction isolation level 為repeatable read,在select 的讀取鎖定主要分為兩種方式 select lock in share modese...
mysql事務與鎖機制 mysql事務與鎖機制
在併發下事務會容易出現的一些問題 資料更新丟失 兩個事務同時操作一條資料,乙個事務因為異常導致資料更新丟失 髒讀 乙個失誤開始讀取了某行資料,另外乙個事務已經更新了此資料但沒有能夠及時提交。這是相當危險的,因為很可能所有的操作都被回滾。不可重複讀 乙個事務對同一行資料重複讀取兩次,但是卻得到了不同的...