在高併發的場景下,經常會遇到這種情況:
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。