執行SELECT語句時沒有加鎖,只有在執行UPDATE時才進行加鎖的。所以才會出現並發操作時的更新數據不一致
a = SELECT * FROM table1 WHERE id=1;
UPDATE table1 SET num = a.num + 1 WHERE id=1;
解決的方法可以有2種:
通過事務顯式的對SELECT進行加鎖
使用樂觀鎖機制
對SELECT進行加鎖的方式有兩種,如下:
SELECT ... LOCK IN SHARE MODE #共享鎖,其它事務可讀,不可更新
SELECT ... FOR UPDATE #排它鎖,其它事務不可讀寫
默認情況下SELECT語句是不會加鎖的。並且對於上面提到的場景,必須使用排它鎖。另外,上面的2種語句只有在事務之中才能生效,否則不會生效。在MySQL命令行使用事務的方式如下:
SET AUTOCOMMIT=0; BEGIN WORK; a = SELECT num FROM table1 WHERE id=2 FOR UPDATE; UPDATE table1 SET num = a.num + 1 WHERE id=2; COMMIT WORK;
只要以后更新數據時,都使用這樣事務來進行操作;那么在並發的情況下,后執行的事務就會被堵塞,直到當前事務執行完成。(通過鎖把並發改成了順序執行)
樂觀鎖
在具體更新數據的時候更新條件中會添加版本號信息,
- 當版本號沒有變化的時候說明該數據行未被更新過,並且也滿足更新條件,所以會更新成功。
- 當版本號有變化的時候,則無法更新數據行,因為條件不滿足,此時就需要在進行一次SQL操作。(重新查詢記數據行,再次使用新的版本號更新數據)