SQL 计数器增量并发问题 - select/update 问题
SQL counter increment concurrency issues - The select/update issue
这更像是一次知识分享post。
最近在我的一个项目中,我遇到了一个很常见的问题,但在我面对它之前从未真正考虑过它。
有许多可用的解决方案,但不知何故我没有找到我正在寻找的解决方案,我将在此 post 中分享。我相信你们中的许多人已经知道以下解决方案,但对于那些不知道的人来说,这绝对可以挽救生命。 :-)
问题:
最近,我正在开发一项 windows 服务。该服务应该将一些记录插入到长期存在并被其他几个旧服务使用的 table 中。有一列(比如“ID”)包含一个整数值。将整数插入此 table 的逻辑(可能是几年前)如下所述:
逻辑:
- 从 table 中读取一个值(比如 Table 1 – Name/Value 对),
- 增加 1
- 更新值并
- 终于得到新的价值[/highlight]。
代码
SELECT @value = [Value] from [dbo].[Table1] WHERE [Name] = @Name;
UPDATE [dbo].[ Table1] SET [Value] = @value +1 WHERE [Name] = @Name;
SELECT @value = @value+1;
使用的技术存在主要并发问题。
- 同一个值被多个线程读取
- 死锁问题
解决此类问题的最佳方法是什么 - 考虑到许多不同的 applications/services?
决议
显而易见的想法是去掉这部分并重写它。但有时,尤其是当你在这样的领域工作时,某些逻辑 tables 代码已经被其他几个程序引用,这不是一个容易的调用。
我的目标是解决问题的根源并摆脱它。如果我能做到这一点,应该不会产生真正的影响。 proc 的输出将是相同的,所以对于外界来说实际上没有任何改变。 :-)
所以,我对上面的语句做了一些小的调整,以确保每个线程都更新值并读取更新后的值。不再有冲突,不再有脏读。
更新代码
DECLARE @table table(
Value int NOT NULL
);
-- Update the value and output the same into a local table variable
UPDATE [dbo].[Table1] SET [Value] = [Value] + 1 OUTPUT inserted.[Value] INTO @table
WHERE [Name] = @Name
-- Now read the value from the local table variable
SELECT Value FROM @table;
有关输出子句的更多信息,请检查https://msdn.microsoft.com/en-us/library/ms177564(v=sql.90).aspx
我运行 一些测试parallel.foreach(20,000 次)。我的测试 table 有两个条目 -
- Name = First 和 Value = 1 - 20,000 次更新后,该值应为 20,001
- 名称 = Second,值 = 100 - 20,000 次更新后,值应为 20,100
如果你查看附件,你会发现数字上的差异。
死锁问题很难重现,但多线程读取同一个值的问题从测试结果中可以明显看出。
欢迎就此提出任何想法。
正如我之前所说,你们中的许多人可能已经知道这个解决方案,但对于那些不知道的人来说,这绝对可以节省一些时间。 :-)
这更像是一次知识分享post。
最近在我的一个项目中,我遇到了一个很常见的问题,但在我面对它之前从未真正考虑过它。 有许多可用的解决方案,但不知何故我没有找到我正在寻找的解决方案,我将在此 post 中分享。我相信你们中的许多人已经知道以下解决方案,但对于那些不知道的人来说,这绝对可以挽救生命。 :-)
问题:
最近,我正在开发一项 windows 服务。该服务应该将一些记录插入到长期存在并被其他几个旧服务使用的 table 中。有一列(比如“ID”)包含一个整数值。将整数插入此 table 的逻辑(可能是几年前)如下所述:
逻辑:
- 从 table 中读取一个值(比如 Table 1 – Name/Value 对),
- 增加 1
- 更新值并
- 终于得到新的价值[/highlight]。
代码
SELECT @value = [Value] from [dbo].[Table1] WHERE [Name] = @Name;
UPDATE [dbo].[ Table1] SET [Value] = @value +1 WHERE [Name] = @Name;
SELECT @value = @value+1;
使用的技术存在主要并发问题。
- 同一个值被多个线程读取
- 死锁问题
解决此类问题的最佳方法是什么 - 考虑到许多不同的 applications/services?
决议
显而易见的想法是去掉这部分并重写它。但有时,尤其是当你在这样的领域工作时,某些逻辑 tables 代码已经被其他几个程序引用,这不是一个容易的调用。
我的目标是解决问题的根源并摆脱它。如果我能做到这一点,应该不会产生真正的影响。 proc 的输出将是相同的,所以对于外界来说实际上没有任何改变。 :-)
所以,我对上面的语句做了一些小的调整,以确保每个线程都更新值并读取更新后的值。不再有冲突,不再有脏读。
更新代码
DECLARE @table table(
Value int NOT NULL
);
-- Update the value and output the same into a local table variable
UPDATE [dbo].[Table1] SET [Value] = [Value] + 1 OUTPUT inserted.[Value] INTO @table
WHERE [Name] = @Name
-- Now read the value from the local table variable
SELECT Value FROM @table;
有关输出子句的更多信息,请检查https://msdn.microsoft.com/en-us/library/ms177564(v=sql.90).aspx
我运行 一些测试parallel.foreach(20,000 次)。我的测试 table 有两个条目 -
- Name = First 和 Value = 1 - 20,000 次更新后,该值应为 20,001
- 名称 = Second,值 = 100 - 20,000 次更新后,值应为 20,100
如果你查看附件,你会发现数字上的差异。
死锁问题很难重现,但多线程读取同一个值的问题从测试结果中可以明显看出。
欢迎就此提出任何想法。
正如我之前所说,你们中的许多人可能已经知道这个解决方案,但对于那些不知道的人来说,这绝对可以节省一些时间。 :-)