我们有一个表和一组用于生成pk ID的过程。该表保存最后一个id,并且过程获取id,递增它,更新表,然后返回新增加的id。
此过程可能在交易中。问题是如果我们有回滚,它可能会回滚到在事务期间使用的任何id之前的id(比如从不同的用户或线程生成)。然后当id再次递增时,它将导致重复。
有没有办法从父事务中排除id生成表以防止这种情况发生?
添加我们当前问题的详细信息......
首先,我们有一个系统,我们正在准备将大量数据迁移到。该系统由ms-sql(2008)数据库和textml数据库组成。 sql数据库包含不到3天的数据,而textml充当旧版本的存档。 textml db还依赖于sql db来为特定字段提供id。这些字段当前是Identity PK,并且在发布到texml db之前在插入时生成。我们不希望通过sql清洗所有迁移的数据,因为记录将在流量和数据方面泛滥当前系统。但与此同时,我们无法生成这些id,因为它们是sql server控制的自动递增值。
其次,我们有一个系统要求,它需要我们能够将旧资产从texml数据库中提取出来并将其插入带有原始id的sql数据库中。这样做是为了修正和编辑,如果我们改变id,它将破坏我们无法控制的客户系统下游的关系。当然这一切都是个问题,因为id列是Identity列。
答案 0 :(得分:2)
程序获取id,递增它, 更新表,然后返回 新增的id
这会导致死锁。程序必须增加并返回一个单一的原子步骤,例如。通过在SQL Server中使用OUTPUT子句:
update ids
set id = id + 1
output inserted.id
where name= @name;
您不必担心并发性。以这种方式生成ID这一事实意味着只有一个事务可以增加一个id,因为更新将独占锁定该行。你不能得到重复。您确实获得了所有操作的完整序列化(即没有性能和低吞吐量),但这是一个不同的问题。这就是为什么你应该使用内置机制来生成序列和身份。这些特定于每个平台:MySQL中的AUTO_INCREMENT
,Oracle中的SEQUENCE
,SQL Server中的IDENTITY
和SEQUENCE
(仅限Denali中的序列)等。
更新
当我阅读您的编辑时,您想要控制生成的身份的唯一原因是能够插入已归档的记录。这已经成为可能,只需使用IDENTITY_INSERT
:
允许插入显式值 进入表的标识列
插回旧记录时将其打开,然后将其关闭:
SET IDENTITY_INSERT recordstable ON;
INSERT INTO recordstable (id, ...) values (@oldid, ...);
SET IDENTITY_INSERT recordstable OFF;
至于为什么手动生成的id序列化所有操作:生成id的任何事务都将独占锁定ids表中的行。在第一个事务提交或回滚之前,没有其他事务可以读取或写入该行。因此,在任何时刻只能有一个事务在表上生成id,即。序列化。