sql-server – 我可以依赖读取SQL Server Identity值吗?

前端之家收集整理的这篇文章主要介绍了sql-server – 我可以依赖读取SQL Server Identity值吗?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
TL; DR:下面的问题可归结为:插入行时,在生成新的Identity值和聚集索引中相应行键的锁定之间是否有机会窗口,外部观察者可以看到并发事务插入的较新标识值? (在sql Server中.)

详细版本

我有一个sql Server表,其中包含一个名为CheckpointSequence的Identity列,它是表的聚簇索引的键(它还有许多其他非聚簇索引).行由几个并发进程和线程插入到表中(在隔离级别READ COMMITTED,没有IDENTITY_INSERT).同时,有一些进程定期从聚簇索引读取行,由CheckpointSequence列排序(同样在隔离级别READ COMMITTED,READ COMMITTED SNAPSHOT选项被关闭).

我目前依赖于读取过程永远不会“跳过”检查点的事实.我的问题是:我可以依靠这个房产吗?如果没有,我能做些什么才能成功呢?

示例:当插入具有标识值1,2,3,4和5的行时,在查看值为4之前,读者不得看到值为5的行.测试显示包含ORDER BY的查询CheckpointSequence子句(以及WHERE CheckpointSequence> -1子句)可以在每次读取第4行时可靠地阻塞,但尚未提交,即使第5行已经提交.

我相信至少在理论上,这里可能存在一种可能导致这种假设破裂的竞争条件.遗憾的是,关于Identity的文档并未详细说明Identity如何在多个并发事务的上下文中工作,它只是说“每个新值都是基于当前种子和增量生成的”.和“特定事务的每个新值都与表上的其他并发事务不同.” (MSDN)

我的理由是,它必须以某种方式工作:

>启动事务(显式或隐式).
>生成标识值(X).
>根据标识值对聚集索引执行相应的行锁定(除非锁定升级启动,在这种情况下整个表都被锁定).
>插入行.
>事务已提交(可能需要很长时间),因此会再次删除锁定.

我认为在第2步和第3步之间,有一个非常小的窗口

>并发会话可以生成下一个标识值(X 1)并执行所有剩余步骤,
>因此允许读者准确地在那个时间点读取值X 1,错过X的值.

当然,这种可能性似乎极低;但仍然 – 它可能发生.或者可以吗?

(如果您对上下文感兴趣:这是NEventStore’s SQL Persistence Engine的实现.NEventStore实现了一个仅附加事件存储,其中每个事件都获得一个新的升序检查点序列号.客户端从检查点排序的事件存储中读取事件,以便一旦处理了检查点X的事件,客户端只会考虑“更新”的事件,即检查点X 1及以上的事件.因此,永远不能跳过事件是至关重要的,因为它们是永远不会再考虑.我目前正在尝试确定基于身份的检查点实现是否满足此要求.这些是使用的确切sql语句:Schema,Writer’s query,Reader’s Query.)

如果我是对的并且可能出现上述情况,我只能看到处理它们的两种选择,这两种选择都不令人满意:

>在看到X之前看到检查点序列值X 1时,忽略X 1并稍后再试.但是,因为Identity当然可以产生差距(例如,当事务被回滚时),X可能永远不会出现.
>所以,同样的方法,但在n毫秒后接受差距.但是,我应该假设n的值是多少?

还有更好的想法?

解决方法

When inserting a row,is there a window of opportunity between the generation of a new Identity value and the locking of the corresponding row key in the clustered index,where an external observer could see a newer Identity value inserted by a concurrent transaction?

是.

身份值的分配独立于包含用户事务.这是即使事务被回滚也会消耗身份值的一个原因.增量操作本身受锁存器保护以防止损坏,但这是保护的程度.

在您的实现的特定情况下,在插入的用户事务甚至被激活之前(在获取任何锁之前)进行身份分配(对CMEDSeqGen :: GenerateNewValue的调用).

通过连接调试器同时运行两个插入,允许我在增加和分配标识值后冻结一个线程,我能够重现以下场景:

>会话1获取身份值(3)
>第2节获取身份值(4)
>会话2执行其插入和提交(因此第4行完全可见)
>会话1执行其插入和提交(第3行)

在步骤3之后,在锁定读取提交下使用row_number的查询返回以下内容

在您的实现中,这将导致错误地跳过Checkpoint ID 3.

错误机会的窗口相对较小,但它存在.提供比附加调试器更真实的场景:执行查询线程可以在上面的步骤1之后产生调度程序.这允许第二个线程在原始线程恢复执行其插入之前分配标识值,插入和提交.

为清楚起见,在分配之后和使用之前,没有锁或其他同步对象保护身份值.例如,在上面的步骤1之后,并发事务可以在表中存在行之前使用IDENT_CURRENT之类的T-sql函数来查看新的标识值(甚至是未提交的).

从根本上说,没有比documented更多的身份价值保证:

>每个新值都是根据当前种子生成的.增量.
>特定事务的每个新值都与表上的其他并发事务不同.

真的就是这样.

如果需要严格的事务FIFO处理,您可能别无选择,只能手动序列化.如果应用程序的需求量较少,则可以选择更多选项.在这方面,问题并非100%明确.不过,您可以在Remus Rusanu的文章Using Tables as Queues中找到一些有用的信息.

原文链接:https://www.f2er.com/mssql/79820.html

猜你在找的MsSQL相关文章