2

有时我需要获取唯一 ID 并将其与记录一起存储,但我无法使用标识列。因此,我有一个使用标签字段和整数提供唯一 ID 的表。当需要一个唯一的 ID 时,我调用一个存储过程并传入标签,它会吐出与其关联的下一个 ID。当然,在具有并发事务的环境中可靠这一点很重要。也就是说,存储过程不应该为给定的标签两次返回相同的值。我对事务隔离的有限理解导致我做了以下事情:

1) 将事务隔离级别设置为可序列化

2) 从 UniqueIdTable WHERE label = @inputLabel 中选择 id

3) 更新 UniqueIdTable SET id = id + 1 WHERE label = @inputLabel

4) 返回 2) 中检索到的 id

但这真的安全吗?即使使用可序列化的隔离,两个线程是否仍然可以同时执行步骤 2)?据我了解,最高隔离级别仅保证单个事务将执行而不会遇到幻像行或更改来自其他线程的数据。如果是这种情况,对 GetID 函数的两个同时调用可能会返回相同的值。

我对隔离级别有误解吗?我如何保证不会发生这种情况?


我还有一个问题需要解决。假设我有一个表,其中有一个字段,其中包含第二个表的外键。最初第一个表中的记录在第二个表中没有相应的记录,因此我在该字段中存储 NULL。现在在某个时候,用户运行一个操作,该操作将在第二个表中生成一条记录,并让第一个表链接到它。这始终是一对一的关系,因此如果两个用户同时尝试生成记录,则会创建一条记录并与之链接,而另一个用户会收到一条消息,指出该记录已存在。如何确保不会在并发环境中创建重复项?

4

3 回答 3

1

您可以使用output在更新语句中递增获取 ID 。

update UniqueIdTable
set ID = ID + 1
output deleted.ID
where label = @inputLabel
于 2011-09-25T15:22:57.437 回答
0

我认为您是正确的,两个线程可以在步骤 2 中读取相同的值。我可以想到两种选择:

  1. 在更新语句中为 id 添加一个谓词,以便它仅在值未更改时更新。如果更新没有更新任何记录(不知道如何在 SQL Server 中签入但必须可以),则重试该操作。

  2. 首先执行更新语句。只有一个线程能够执行。然后选择更新的值。

我还有两个建议

  1. 在单独的事务中执行此操作,以便长时间运行的事务不会阻塞另一个事务

  2. 在应用层保留一个线程局部块。以大值递增,然后 1 并使用线程本地块中的 id。这将减少服务器往返和对表的更新

于 2011-09-25T15:15:57.800 回答
0

在某些数据库中创建一个自定义表,您可以在其中包含增量 ID 字段。任何需要此数字的应用程序都将创建一条记录并利用返回的值。即使你取了这个值并且没有将它应用到你需要它的表中,即使你在一年之后应用它,它仍然是唯一的。

于 2011-09-25T20:01:47.727 回答