安全递增ID所需的事务隔离级别

时间:2010-04-28 11:28:36

标签: sql transactions thread-safety

我正在编写一小段软件,用于将记录插入商业应用程序使用的数据库中。相关表中的唯一主键(ID)是顺序的,但似乎没有设置为“自动增量”。因此,我假设,我将不得不找到最大的id,增加它并将该值用于我正在插入的记录。

在伪代码中为简洁起见:

id = select max(id) from some_table
id++
insert into some_table values(id, othervalues...)

现在,如果另一个线程在第一个完成其插入之前启动了同一个事务,那么在尝试插入最后一个时会得到两个相同的ID并且失败。您可以检查该失败并重试,但更简单的解决方案可能是在事务上设置隔离级别。为此,我需要SERIALIZABLE或更低级别吗?

此外,这通常是解决问题的合理方法吗?是否采取其他任何方式?

4 个答案:

答案 0 :(得分:2)

这样做的一种方法是将前两行合并到insert语句中:

INSERT INTO Some_Table VALUES((从Some_Table中选择Max(id)+ 1),Othervalues ...)

INSERT INTO Some_Table SELECT st2.id,Othervalues FROM(从Some_Table中选择max(id)+1)st2

否则,你真正想要做的就是在事务中将其锁定并防止其他人在第一行中读取,最后得到重复的ID ......但是通过阻止读取,你开辟了更大的虫子。

在其他系统中,我看到一个用来存储max键的表 - 这样你就可以在递增时锁定max_keys表并且你的问题消失了 - 但听起来好像你被供应商的表结构所困扰。 / p>

答案 1 :(得分:1)

希望this帮助

答案 2 :(得分:0)

试试这个(使用SQL Server语法):

INSERT INTO some_table 
        (id, othervalues...)
    SELECT
        ISNULL(max(id),0)+1, othervalues...
        from some_table WITH (UPDLOCK, HOLDLOCK)
        WHERE ...

答案 3 :(得分:0)

我们使用单独的表解决了通常顺序(但有时可以覆盖而不是唯一)文档编号的类似问题。 SQL的想法:

Declare @docno Int
Begin Tran
Select @docno=lastval+1 From docnotable With(Updlock) Where doctype='xyz'
Update docnotable Set lastval=@docno Where doctype='xyz'
... do whatever you need ...
Commit Tran