原贴:
点击打开链接
-- 测试表及测试数据开始
USE tempdb
GO
IF OBJECT_ID('t') IS NOT NULL DROP TABLE t
CREATE TABLE t(
id INT IDENTITY(1,1) PRIMARY KEY ,
userId INT NOT NULL,
consume INT NOT NULL,
balance INT NOT NULL
)
--第一条记录视为交费,交费直接插入就好,不需要任何逻辑
INSERT INTO t(userId,consume,balance) VALUES(120,9,1000)
-- 测试表及测试数据结束
----- 插入消费记录 BEGIN -----
-- 用 SqlQueryStress 工具测试 50线程,每线程循环10次
DECLARE @userId INT,@consume INT
SET @userId=120
SET @consume=1
INSERT INTO t(userId,consume,balance)
SELECT @userId,@consume,balance-@consume
FROM t
WHERE userId=@userId
AND id = (SELECT MAX(id) FROM t WITH(TABLOCK) WHERE userId=@userId)
--注:上面With 后面只能加 TABLOCK ,其它锁都会有问题无法通过后面的验证
----- 插入消费记录 END -----
----- 验证开始 -----
--验证是否有操作失误的记录(消费了余额不可能不变化)
SELECT * FROM t a WHERE EXISTS(
SELECT * FROM t b WHERE a.id!=b.id AND a.balance=b.balance
)
--无记录
SELECT TOP 1 * FROM t ORDER BY id DESC
/*
id userId consume balance
501 120 1 500
*/
----- 验证结束 -----
后记:
TABLOCK的效率是最低的,不过按楼主的需求,只有表锁才能满足要求了。
建议用户表中增加一个字段:余额,每次更新用户表, 只需要锁定一行, 而且根本不需要写显式的锁和事务。 效率高很多。
更新完用户表, 往现在的这个表里面插入一条记录即可。
附:锁的相关知识:点击打开链接