7

在这些条件下,sql中会出现竞争条件吗?

如果我在一个线程中运行此 SQL 更新,则将其称为语句 1:

Update Items
Set Flag = B
where Flag = A;

这个 SQL 更新在另一个调用它的语句 2 中运行:

Update Items
Set Flag = C
where Flag = A;

每个线程是否可以读取 Flag 等于 A 的同一记录并用自己的值写入记录?这样语句 1 可以先写,然后语句 2 写,反之亦然?

这个问题的答案取决于数据库何时以独占方式锁定更新。它是在找到记录之前发生还是在找到记录并评估 where 子句之后发生?

4

4 回答 4

5

首先,存在三种锁上下文:

  • 数据库级锁
  • 表级锁
  • 行级锁

然后你有四种锁定模式:

  • X
  • 小号

IX 和 IS 锁是“意图”锁。这些锁在获取其他类型的锁之前被持有。X 锁是排他(写)锁,S 锁是共享(读)锁。

可以在任何上下文级别获取锁(IX、IS、X 或 S)锁。例如,数据库级别的 X 锁将阻止数据库中的所有其他操作。这是 SQLlite 采用的锁类型。在读取期间为整个数据库获取 S 锁,在写入期间为整个数据库获取 X 锁。写入将等待任何 S 锁完成,并将阻止新的 S 和 X 锁,直到释放写入锁为止。这提供了可序列化的隔离事务级别。

对于 MySQL,锁定取决于存储引擎。MyISAM 将在整个(组)表上使用 X 和 S 锁。X 锁将等待现有的 S 或 X 锁并阻止新锁。新的 X 锁将在队列中被赋予更高的优先级,排在新的 S 锁之前。可以通过设置 LOW_PRIORITY_UPDATES 来更改此行为,这可能会导致写入不足,因为写入将被取消优先级以支持读取。

在 MySQL 中,可以使用'FLUSH TABLES WITH READ LOCK'获得整个数据库的 X 锁。

InnoDB 在通过索引读取遇到行时锁定行。InnoDB 锁定索引记录,并在遍历索引记录时锁定记录。InnoDB 使用称为“间隙”锁的特殊锁来确保 REPEATABLE-READ 事务隔离级别。锁定在索引条目上,因此如果一个表没有很好地为 UPDATE 查询建立索引,那么许多行将被锁定。请注意,InnoDB 不会为正常的 SELECT 查询创建 S 锁。它使用行版本控制,而不是行级锁定来获得一致的快照。

在获取 X 锁时,数据库需要检测死锁。考虑以下:

>connection 1
start transaction;
update T set c = c + 1 order by id asc;

>connection 2
start transaction;
update T set c = c - 1 order by id desc;

在行锁定模型中,这两条语句不可能都成功完成。第一个将永远等待获取第二个持有的锁,反之亦然。数据库将选择其中一个连接进行回滚。InnoDB 将选择更改次数最少的连接。MyISAM 将为首先获得锁的连接锁定整个表,然后第二个将在第一个完成后运行。

您给出的简单示例将通过任何上下文(数据库、表或行)的 X 锁来解决。如果两个连接以完全相同的类型开始,都运行两个尝试更新同一行的更新,则两者都将尝试获取 X 锁。只有一个连接可以获取 X 锁。无法准确确定哪一个将获得锁。另一个连接必须等到锁被释放,才能获得 X 锁。请记住,如果该行被 DELETE 或 UPDATE 锁定,那么等待者在等待后可能最终没有获得锁,因为数据库中没有任何东西可以锁定。

在您的示例中,第一个 UPDATE 获取 X 锁,然后第二个 UPDATE 将等待 X 锁并最终执行但不匹配任何行。

于 2012-08-04T00:12:10.933 回答
0

所有 SQL 数据库都几乎可以保证不会发生这种冲突。“何时”发生锁定取决于锁定是在表、分区、页还是行级别。或者,您是否在数据库中关闭了此类锁定。

如果您有并发更新语句并且要更新多行,则可能发生的情况是,第一行更新了第一行,第二行更新了一些行。

一般来说,我认为 where 子句被评估为选择行集,一次锁定一个行,进行更新和解锁。但是,这取决于锁定的类型。在这种情况下,上述情况将继续值翻转。

如果您担心这种情况,请在处理并发更新请求时使用表级锁定来强制序列化。

于 2012-08-03T22:40:39.000 回答
0

听起来您应该阅读有关锁定的内容。SQL Server 有一组复杂的逻辑,将根据它估计需要更新的行数来执行表或行级别的锁定。除非您明确告诉它您希望它执行哪个,否则它甚至可能因查询而异。通常,如果您要修改表的一小部分,它将选择行级锁。

SQL Server 在设计时考虑了 ACID,因此它会在对数据执行任何实际更新之前将更改写入其日志。这允许回滚任何失败的更新,并允许查询之间的一致性(就像您询问的那样)。您可以执行脏读来解决锁定问题,但是您不能阻止 SQL Server 锁定插入、更新和/或删除的记录。

SQL Server 锁定

编辑:这是一篇关于 ACID 的文章。 酸 - 维基百科

于 2012-08-03T22:30:42.713 回答
0

排他锁,用于数据修改操作,如 INSERT、UPDATE 或 DELETE,将在此场景中使用。

排他锁确保不能同时对同一资源进行多次更新。

在这种情况下,您不会获得竞争条件。

如果您有一个涉及多个表的更复杂的场景,那么您可能会遇到竞争条件或死锁。有很多方法可以避免这种情况,简化和分离查询等。

您还可以将提示应用于告诉 SQL 使用哪种类型的锁的查询。

http://msdn.microsoft.com/en-us/library/aa213026(v=sql.80).aspx

于 2012-08-03T22:28:27.217 回答