SQL唯一字段:并发错误



我有一个DB表,其中的字段必须是唯一的。假设该表被称为"Table1",唯一字段被称为"Field1"。

我计划通过执行SELECT来实现这一点,看看是否存在Field1=@valueForField1的Table1记录,如果不存在这样的记录,则只更新或插入。

问题是,我怎么知道这里没有比赛条件?如果两个用户都在写入Table1的表单上单击"保存"(几乎同时),并且他们的Field1值相同,难道不可能发生以下情况吗?

User1进行SQL调用,该调用执行选择操作,并确定没有Field1=@valueForField1的现有记录。User1的进程被User2的进程抢占,该进程也找不到Field1=@valueForField1的记录,并执行插入。User1的进程被允许再次运行,并插入第二条记录,其中Field1=@valueForField1,这违反了Field1唯一的要求。

我该如何防止这种情况发生?有人告诉我事务是原子的,但为什么我们也需要表锁呢?我以前从来没有用过锁,我不知道这种情况下我是否需要一把。如果进程试图写入锁定的表,会发生什么情况?它会阻塞并重试吗?

我使用的是MS SQL 2008R2。

在字段上添加唯一约束。这样你就不必选择了。您只需要插入即可。第一个用户将成功,第二个用户将失败。

最重要的是,你可以使字段自动递增,这样你就不必关心填充它,或者你可以添加一个默认值,同样不关心填充它。

一些选项可以是自动递增的INT字段或唯一标识符。

您可以添加一个添加唯一约束。示例来自http://www.w3schools.com/sql/sql_unique.asp:

CREATE TABLE Persons
(
P_Id int NOT NULL UNIQUE
)
编辑:也请阅读下面马丁·史密斯的评论。

jyparask对如何解决这个特定问题给出了很好的答案。但是,我想详细说明一下您对锁、事务、阻塞和重试的困惑。为了简单起见,我假设事务隔离级别是可序列化的。

事务是原子的。数据库保证,如果您有两个事务,那么无论存在何种竞争条件,一个事务中的所有操作都会在下一个事务开始之前完全发生。即使两个用户同时访问同一行(多个核心),也不可能出现竞争条件,因为数据库将确保其中一个会失败。

数据库是如何做到这一点的?带锁。当您选择一行时,SQL Server将锁定该行,以便所有其他客户端在请求该行时都会被阻止。块意味着他们的查询将暂停,直到该行被解锁。

数据库实际上有几个可以锁定的东西。它可以锁定行、表或两者之间的某个位置。数据库决定它认为什么是最好的,而且它通常非常擅长

永远不会有任何重试。数据库永远不会为您重试查询。您需要明确地告诉它重试查询。原因是很难定义正确的行为。查询是否应该使用完全相同的参数重试?还是应该修改一些内容?重试查询仍然安全吗?对于数据库来说,简单地抛出一个异常并让您处理它要安全得多

让我们来举你的例子。假设您正确地使用了事务并进行了正确的查询(Martin Smith链接到了一些好的解决方案),那么数据库将创建正确的锁,从而消除竞争条件。一个用户将成功,另一个将失败。在这种情况下,没有阻塞,也没有重试。

然而,在事务的一般情况下,会有阻塞,您可以实现重试。

最新更新