我被困在设置事务隔离级别。这是我在应用程序中发生的场景:
- 获取未处理的消息(使用
IsProcessing
标志) - 将它们设置
IsProcessing
为 true(在 RAM 中)并更新它们的IsProcessing
状态 - 做生意
- 将它们设置
IsProcessing
为 false(在 RAM 中)并更新它们的IsProcessing
状态
当按顺序运行时,这可以正常工作。但是当我运行我的应用程序的多个实例(并发)时,我看到一些消息被处理了两次或三次。这是发生的事情:
- 实例 A 收到一些未处理的消息
- 当实例 A
IsProcessing
在 RAM 中将
这就是我为了防止它所做的事情:
- 开始事务(可序列化)
- 获取未处理的消息(使用
IsProcessing
标志) - 将它们设置
IsProcessing
为 true(在 RAM 中)并更新它们的IsProcessing
状态 - 提交事务
- 做生意
- 将它们设置
IsProcessing
为 false(在 RAM 中) - 开始事务(可序列化)
- 更新他们的
IsProcessing
状态 - 提交事务
我不知道为什么,但是在步骤1到4期间,其他实例仍然可以执行读取查询。这是不希望的。我想专门阻止任何事情,甚至在步骤 1 到 4 期间对消息表执行读取查询。
我怎样才能做到这一点?我的设计中缺少什么?目标是确保当消息排队等待处理时,没有其他实例会再次处理它。
通过在单个语句中执行许多步骤,您将能够避免许多竞争条件。通过使用
TOP()
cluse 最多可以在一行上设置标志。通过使用OUTPUT
cluse,您可以自动将其返回给应用程序。我定义了一个简单的测试表并填充它:
输出子句需要一个表变量来接收更改的值:
一些调试代码使“之前”和“之后”状态变得明显:
声明本身:
结果:
这是上述第二次执行的输出。
id=2
带有fromIsProcessing=0
toIsProcessing=1
的行在id
table 变量中返回。使用这些微不足道的数据,行将按照它们创建的顺序进行处理。在更复杂的环境中,优化器可以选择与 where 子句匹配的任何行。如果您需要处理时间序列中的行,则需要进一步的资格。
我一直没有考虑到这一点,但我相信无论有没有明确的事务,他都可以在任何隔离级别上工作。
IsProcessing=0
当然,在语句运行时可能没有行。在这种情况下,表变量将有零行。要完全隔离每个事务,您可以尝试
sp_getapplock
. 这将增加开销并降低并发性。在成功和失败的情况下,您都必须小心尽快释放应用程序锁定。