在高並發下,經常需要處理SELECT之后,在業務層處理邏輯,再執行UPDATE的情況。
若兩個連接並發查詢同一條數據,然后在執行一些邏輯判斷或業務操作后,執行UPDATE,可能出現與預期不相符的結果。
在不使用悲觀鎖與復雜SQL的前提下,可以使用樂觀鎖處理該問題,同時兼顧性能。
場景模擬:
假設一張表兩個字段,一個id,一個use_count。表里存了100個id,每個id對應自己的use_count。
當id每使用一次,use_count要加1。當use_count大於1000時,這個id就不能在被使用了(換句話說 無法從數據庫中查出)。
在高並發情況下,會遇到一種問題:假設數據表中有一條記錄為:id=123456, use_count=999 A與B兩個連接並發查詢這個id=123456,都執行下列SQL:
1
|
SELECT
*
FROM
table
WHERE
id=123456
and
use_count < 1000;
|
A先執行,得到id=123456的use_count是999,之后在程序里做了一些邏輯判斷或業務操作后執行SQL:
1
|
UPDATE
table
SET
use_count + 1
WHERE
id=123456;
|
在A做判斷且沒有update之前,B也執行了查詢SQL,發現use_count是999,之后它也會執行SQL:
1
|
UPDATE
table
SET
use_count + 1
WHERE
id=123456;
|
但是,事實上B不應該取得這個id,因為A已經是第1000個使用者。
處理步驟如下:
1、添加第3個字段version,int類型,default值為0。version值每次update時作加1處理。
1
|
ALTER
TABLE
table
ADD
COLUMN
version
INT
DEFAULT
'0'
NOT
NULL
AFTER
use_count;
|
2、SELECT時同時獲取version值(例如為3)。
1
|
SELECT
use_count, version
FROM
table
WHERE
id=123456
AND
use_count < 1000;
|
3、UPDATE時檢查version值是否為第2步獲取到的值。
1
|
UPDATE
table
SET
version=4, use_count=use_count+1
WHERE
id=123456
AND
version=3;
|
如果UPDATE的記錄數為1,則表示成功。如果UPDATE的記錄數為0,則表示已經被其他連接UPDATE過了,需作異常處理。