我有一个DB表,其字段必须是唯一的。假设该表称为“Table1”,唯一字段称为“Field1”。
我计划通过执行SELECT来查看是否存在任何Table1记录,其中Field1 = @ valueForField1,并且只有在没有这样的记录时才更新或插入。
问题是,我怎么知道这里没有竞争条件?如果两个用户都在写入Table1的表单上单击“保存”(几乎完全相同的时间),并且它们具有相同的Field1值,则不可能发生以下情况吗?
User1进行SQL调用,执行select操作并确定Field1 = @ valueForField1中没有现有记录。 User1的进程被User2的进程抢占,该进程也找不到Field1 = @ valueForField1的记录,并执行插入。允许User1的进程再次运行,并在Field1 = @ valueForField1中插入第二条记录,违反了Field1唯一的要求。
我该怎样防止这种情况?我被告知交易是原子的,但为什么我们也需要表锁?我之前从未使用过锁,我不知道在这种情况下是否需要锁。如果进程尝试写入锁定表会发生什么?它会阻止并重试吗?
我正在使用MS SQL 2008R2。
答案 0 :(得分:5)
在字段上添加唯一约束。那样你就不用SELECT了。你只需要插入。第一个用户将成功,第二个用户将失败。
最重要的是,您可以自动增加字段,因此您无需关心填充字段,或者您可以添加默认值,再次不关心填充它。
一些选项可能是自动增量的INT字段或唯一标识符。
答案 1 :(得分:0)
您可以添加添加唯一约束。 http://www.w3schools.com/sql/sql_unique.asp的示例:
CREATE TABLE Persons
(
P_Id int NOT NULL UNIQUE
)
答案 2 :(得分:0)
编辑:请阅读下面的Martin Smith的评论。
jyparask对如何解决这个具体问题有一个很好的答案。但是,我想详细说明您对锁,事务,阻塞和重试的困惑。为简单起见,我将假设事务隔离级别可序列化。
交易是原子的。数据库保证,如果您有两个事务,那么一个事务中的所有操作都会在下一个事务开始之前完全发生,无论哪种竞争条件存在。即使两个用户同时访问同一行(多个核心),也不存在竞争条件,因为数据库将确保其中一个失败。
数据库是如何做到这一点的?带锁。当您选择一行时,SQL Server将锁定该行,以便所有其他客户端在请求该行时将阻止。阻止意味着他们的查询暂停,直到该行被解锁。
数据库实际上有一些可以锁定的东西。它可以锁定行或表,或其间的某个位置。数据库决定它认为什么是最好的,而且它通常很擅长。
永远不会有任何重试。数据库永远不会为您重试查询。您需要明确告诉它重试查询。原因是因为正确的行为很难定义。查询是否应使用完全相同的参数重试?或者应该修改一些东西?重试查询仍然安全吗?数据库简单地抛出异常并让你处理它会更安全。
让我们来谈谈你的榜样。假设您正确使用事务并执行正确的查询(Martin Smith链接到一些好的解决方案),那么数据库将创建正确的锁,以便竞争条件消失。一个用户将成功,另一个将失败。在这种情况下,没有阻止,也没有重试。
但是,在事务的一般情况下,会有阻塞,您可以实现重试。