1. 程式人生 > >淺談 SQL 中的鎖(一)餘額問題

淺談 SQL 中的鎖(一)餘額問題

這兩天看到資料庫的資料一致性和鎖定的問題:
http://bbs.csdn.net/topics/392072953
http://bbs.csdn.net/topics/392073296

寫一個帖子專門說說這個。

舉一個使用者餘額的例子,餘額可以是現金、虛擬幣、積分等,可以看作對資料準確度要求高的資料,一旦出錯,就會造成客戶或公司的直接損失。

在 SQL Server 建立一個測試表

--測試用餘額表
create table balance
(
	id int primary key,	--使用者ID
	value int			--餘額
)

--插入測試資料
delete balance
insert balance values(1, 1)

ID 為 1 的使用者有 1 塊錢的餘額

執行一個【假如使用者的餘額超過或等於 1 元,則扣除 1 元】的操作

--檢查使用者的餘額是否足夠
if(exists(select * from balance where id = 1 and value >= 1))
begin
	--延長處理時間
	waitfor delay '0:00:10'

	--更新餘額
	update balance set value = value - 1
end

--檢視處理結果
select * from balance

在 10 秒之內,另開一個連線,再次執行上面的操作。這是一個併發的操作,在語句模擬的處理時間之內執行。

第二次操作之後,發現使用者的餘額變成了 -1,這種情況是不能容忍的。

出現這個問題的原因是,在兩個併發的操作中,檢查餘額的語句執行的時候,使用者的餘額都是足夠的,因此兩個扣錢的操作都執行了。

從技術的角度上來說,就是兩個操作並沒有互斥,本來第二個操作應該在第一個操作完成扣錢之後,再去查詢餘額的。

其實資料庫是可以做這樣的互斥的,只不過這樣做會犧牲效能,於是大部分資料庫的隔離級別是【讀取提交(READ COMMITTED)】,查詢語句也是使用的共享鎖。

預設的選擇是一種適合大多數情況的,平衡的做法。但顯然不適合這種計算餘額的情況。

事務型資料庫早就想到了這一點,只要在 SQL 語句做一點修改,就可以解決這個問題。