我有一个应用程序,当一个新用户被添加到某个位置时,会为其分配一个序号。因此,例如,位置01的第一个用户将被分配01-0001,第二个用户将被分配01-0002等。
尽管我很容易找到该位置和+1的最大用户数,但我的问题是我需要这个是线程/冲突安全的。
虽然它并不常见,但我不希望一个查询找到max()号,而另一个查询正在同一时刻添加该号码。 (它发生在我原来的应用程序之前,虽然在5年内只发生过两次。)
最好的方法是什么?我宁愿不依赖于一个独特的约束,因为它会抛出错误并迫使进程再次尝试。
答案 0 :(得分:4)
您可以使用
BEGIN TRAN
SELECT @UserId = MAX(UserId)
FROM YourTable WITH(UPDLOCK, HOLDLOCK, ROWLOCK)
WHERE LocationId = @LocationId
--TODO: Increment it or initialise it if no users yet.
INSERT INTO YourTable (UserId, Name)
VALUES (@UserId, @Name)
COMMIT
一次只有一个会话可以对资源进行更新锁定,因此如果两个并发会话尝试为同一位置插入一行,则其中一个将被阻塞,直到另一个会话提交为止。 HOLDLOCK
是给出可序列化的语义并阻止包含最大值的范围
这是一个潜在的瓶颈,但这是设计因为要求数字是连续的。性能更好的替代方案,例如IDENTITY
,并不能保证这一点。实际上虽然听起来好像你的应用程序使用率很低,但这可能不是一个大问题。
另一个可能的问题是,如果某个位置的当前最大值的用户被删除,那么该ID将被回收,但这对于现有应用程序的声音是相同的,所以我认为这不是问题或者只是没有发生。
答案 1 :(得分:0)
您可以使用sequence
描述的create sequence dbo.UserId as int
start with 1
increment by 1;
对象。
创建一个新的序列非常简单,例如你可以使用这个代码
sequence
使用NEXT VALUE FOR
对象,您无需了解任何碰撞。每次使用select next value for dbo.UserId;
语句获取时,Sequence将始终返回下一个值,就像在此代码中一样
SELECT
即使您的回滚事务或即使您在单独的并行事务中获得下一个值,下一个值也将正确返回。