關於update操作併發問題

在高併發的場景下,經常會遇到這種情況:
A請求過來,查詢出來一條數據,進行update操作,與此同時B請求也在這個時候過來,對這條數據進行查詢,並進行操作。此時就會出現B在A之後進行查詢操作,但是實際B的數據卻被A覆蓋。

表名A,字段名爲 number,如下的SQL語句:

甲操作 語句1:select num from store where id='1';

假設此時甲獲取到 num= 99

乙操作 語句2:select num from store where id='1';

因爲甲方還沒有update操作,乙獲也取到 num= 99
這時候A進行update操作

update store set num =${num} +1 where id='1';

這時候寫入數據庫的num即爲100
此時B請求也發起了更新操作:

update store set num =${num} +1 where id='1';

這時候我們的預期本應該是101的,但是實際上B又在數據庫寫入了100

解決方案:

(1)引入一個版本號的概念,在表A中增加一個version字段

甲操作 語句1:select num,version from store where id='1';

假設此時甲獲取到 num= 99 version =1

乙操作 語句2:select num,version from store where id='1';

因爲甲方還沒有update操作,乙獲也取到 num= 99 version=1
這時候A進行update操作

update store set num =${num} +1 where id='1' and version = ${version};

這時候寫入數據庫的num即爲100, version =2
此時B請求也發起了更新操作:

update store set num =${num} +1 where id='1' and version = ${version} ;

這時候發現條件version = 1不成立,因爲上一步操作version已經爲2了,所以update無法更新。

(2)解決方式:update A set number=number+1 where id=1; 語句直接處理

表名A,字段名爲 number,如下的SQL語句:

語句1:update A set number=number+1 where id=1;
語句2:update A set number=number+2 where id=1;

假設這兩條SQL語句同時被mysql執行,id=1的記錄中number字段的原始值爲99,那麼是否有可能出現這種情況:
語句1和2因爲同時執行,他們得到的number的值都是99,都是在10的基礎上分別加1和2,導致最終number被更新爲100或101,而不是102

這個其實就是 關係型數據庫本身就需要解決的問題。首先,他們同時被MySQL執行,你的意思其實就是他們是併發執行的,而併發執行的事務在關係型數據庫中是有專門的理論支持的- ACID,事務並行等理論,所有關係型數據庫實現,包括Oracle, MySQL都需要遵循這個原理。
簡單一點理解就是鎖的原理。這個時候第一個update會持有id=1這行記錄的 排它鎖,第二個update需要持有這個記錄的排它鎖的才能對他進行修改,正常的話, 第二個update會阻塞,直到第一個update提交成功,他纔會獲得這個鎖,從而對數據進行修改。
也就是說,按照關係型數據庫的理論,這兩個update都成功的話,id=1的number一定會被修改成22。如果不是22, 那就是數據庫實現的一個嚴重的bug。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章