1. 程式人生 > >【轉載】MySQL事務以及SELECT ... FOR UPDATE的使用

【轉載】MySQL事務以及SELECT ... FOR UPDATE的使用

商品 tail ase -a base 我們 evel erl tel

MySQL中的事務,默認是自動提交的,即autocommit = 1; 但是這樣的話,在某些情形中就會出現問題:比如: 如果你想一次性插入了1000條數據,mysql會commit1000次的, 如果我們把autocommit關閉掉[autocommit = 0],通過程序來控制,只要一次commit就可以了,這樣也才能更好的體現事務的特點! 對於需要操作數值,比如金額,個數等等! 記住一個原則:一鎖二判三更新 如果SELECT 後面若要UPDATE 同一個表單,最好使用SELECT ... FOR 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 就可以這樣測試(請確保數據庫表的引擎是Innodb而不是MyIsAM): SET AUTOCOMMIT=0; BEGIN WORK; SELECT user_tel FROM users WHERE user_id=‘60‘ FOR UPDATE; ================================================================================= 此時users數據中id=60 的數據被鎖住,其它事務必須等待此次事務 提交後才能執行! 另外開一個窗口測試, 1. SELECT * from users where user_id=‘60‘ FOR UPDATE; 因為上面事務還沒有提交,id =60的數據已經被鎖住,此處再執行的話數據庫
會報錯!
[Err] 1205 - Lock wait timeout exceeded; try restarting transaction 2.在另外一個窗口中來對表進行更新操作一樣不能成功,會返回同樣的錯誤。 3.SELECT * FROM usersWHERE id=60 則不會受影響! SELECT * FROM users WHERE id=60 FOR UPDATE 如此可以確保user_tel在別的事務讀到的數字是正確的。 ================================================================================ 必須在鎖表語句的同一個回話裏執行更新操作才能夠釋放鎖 UPDATE users SET user_tels= ‘111212122‘ WHERE id=60 ; COMMIT WORK; ================================================================================== 上面介紹過SELECT ... FOR UPDATE 的用法,不過鎖定(Lock)的數據是判別就得要註意一下了。 由於InnoDB[MySQL數據庫引擎之一] 預設是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 ,開二個視窗來做測試。 可以對比下Oracle中的for update http://blog.csdn.net/winy_lm/article/details/48175885 以上

【轉載】MySQL事務以及SELECT ... FOR UPDATE的使用