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) 我的理由是,它必须以某种方式工作: >启动事务(显式或隐式). 我认为在第2步和第3步之间,有一个非常小的窗口 >并发会话可以生成下一个标识值(X 1)并执行所有剩余步骤, 当然,这种可能性似乎极低;但仍然 – 它可能发生.或者可以吗? (如果您对上下文感兴趣:这是NEventStore’s SQL Persistence Engine的实现.NEventStore实现了一个仅附加事件存储,其中每个事件都获得一个新的升序检查点序列号.客户端从检查点排序的事件存储中读取事件,以便一旦处理了检查点X的事件,客户端只会考虑“更新”的事件,即检查点X 1及以上的事件.因此,永远不能跳过事件是至关重要的,因为它们是永远不会再考虑.我目前正在尝试确定基于身份的检查点实现是否满足此要求.这些是使用的确切SQL语句:Schema,Writer’s query,Reader’s Query.) 如果我是对的并且可能出现上述情况,我只能看到处理它们的两种选择,这两种选择都不令人满意: >在看到X之前看到检查点序列值X 1时,忽略X 1并稍后再试.但是,因为Identity当然可以产生差距(例如,当事务被回滚时),X可能永远不会出现. 还有更好的想法? 解决方法
是. 身份值的分配独立于包含用户事务.这是即使事务被回滚也会消耗身份值的一个原因.增量操作本身受锁存器保护以防止损坏,但这是保护的程度. 在您的实现的特定情况下,在插入的用户事务甚至被激活之前(在获取任何锁之前)进行身份分配(对CMEDSeqGen :: GenerateNewValue的调用). 通过连接调试器同时运行两个插入,允许我在增加和分配标识值后冻结一个线程,我能够重现以下场景: >会话1获取身份值(3) 在步骤3之后,在锁定读取提交下使用row_number的查询返回以下内容: 在您的实现中,这将导致错误地跳过Checkpoint ID 3. 错误机会的窗口相对较小,但它存在.提供比附加调试器更真实的场景:执行查询线程可以在上面的步骤1之后产生调度程序.这允许第二个线程在原始线程恢复执行其插入之前分配标识值,插入和提交. 为清楚起见,在分配之后和使用之前,没有锁或其他同步对象保护身份值.例如,在上面的步骤1之后,并发事务可以在表中存在行之前使用IDENT_CURRENT之类的T-SQL函数来查看新的标识值(甚至是未提交的). 从根本上说,没有比documented更多的身份价值保证: >每个新值都是根据当前种子生成的.增量. 真的就是这样. 如果需要严格的事务FIFO处理,您可能别无选择,只能手动序列化.如果应用程序的需求量较少,则可以选择更多选项.在这方面,问题并非100%明确.不过,您可以在Remus Rusanu的文章Using Tables as Queues中找到一些有用的信息. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |