2012-08-03 58 views
4

在这些情况下sql可能出现竞态条件吗?sql何时独占地锁定更新语句中的一行?

如果我有一个线程调用它的语句1运行这个SQL更新:

更新项目 设置标志= B 其中标志= A;

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

更新项目 设置标志= C 其中标志= A;

是否有可能让每个线程读取Flag等于A的相同记录并用自己的值写入记录?这样的陈述1可以先写,然后陈述2写或反之亦然?

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

+10

你正在使用哪个数据库? – 2012-08-03 22:16:35

+0

第二条陈述将等到第一条陈述提交。而且它真的很容易使用您正在使用的DBMS进行尝试。 – 2012-08-03 22:23:05

+0

http://docs.oracle.com/cd/B10501_01/server.920/a96524/c21cnsis.htm – Samson 2012-08-03 22:51:39

回答

0

在此场景中将使用用于数据修改操作(例如INSERT,UPDATE或DELETE)的独占锁定。

独占锁确保多个更新不能同时对同一资源进行。

在这种情况下,您不会得到竞争状态。

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

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

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

+0

因此,在找到与where子句相匹配的记录之前就会提出排它锁定?我们确实知道吗? – Litehouse 2012-08-04 00:06:44

0

听起来像是你应该阅读有关锁定。 SQL服务器具有一组复杂的逻辑,并根据它估计需要更新的行数执行表级或行级锁。除非你明确告诉它你希望它执行什么,否则甚至可能因查询而异。通常如果你修改表的一小部分,它会选择一个行级锁。

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

SQL Server Locking

编辑:下面是关于ACID的文章。 ACID - Wikipedia

0

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

如果您有并发更新语句和多行更新,会发生什么情况是一行更新了第一行,一行更新了第二行。

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

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

+0

没有符合SQL标准的数据库将“翻转”这些值。第一个要执行的语句将获取锁,更改值并释放提交时的锁。一些数据库会占用额外的锁。例如,取决于事务隔离级别,范围之前和之后的“间隔”可能必须被锁定。 – 2012-08-04 01:06:21

+0

我没有说这是标准的行为。无论如何,这是我的困惑。至少在一个数据库(SQL Server)上更新时允许读取未提交的提示...但该提示被忽略。 – 2012-08-04 04:21:18

+0

在某些数据库中,MVCC用于SELECT语句,但不适用于其他DML。由于这个原因,MySQL InnoDB非SELECT DML是“半可重复”读取的,因为最新版本的行总是呈现给非SELECT语句。在任何情况下都不会出现两个连接在同一行上获得X锁的情况。如果发生这种情况,那么就必须宣布一个僵局。 – 2012-08-04 04:29:00

4

首先,有三个锁背景:

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

那么您有四个锁模式:

  • IX
  • IS
  • X
  • 小号

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

锁定(IX,IS,X或X)锁定可以在任何上下文级别进行。例如,数据库级别的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使用称为'gap'锁的特殊锁确保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锁定,那么服务器可能最终在等待后不会获取锁,因为数据库中没有任何内容可以锁定。

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

+0

是的,但是他们什么时候尝试获取X锁? where子句在评估之前还是之后?这就是我试图理解的。 – Litehouse 2012-08-04 00:25:29

+0

这取决于数据库。如果使用SQLlite,则在评估WHERE子句之前获取X锁,因为该锁是DATABASE级别的。如果使用MyISAM,则在计算WHERE子句之前采用X锁,因为X子句是TABLE级锁。如果您使用InnoDB(行级锁),则在评估WHERE子句期间会采用X锁,因为如果没有通过计算WHERE子句来查找行,您无法确定要锁定哪些行。这就是为什么你用MyISAM获得序列化的更新,但是在我的死锁例子中与InnoDB发生了死锁。 – 2012-08-04 00:31:28

+0

那么在InnoDB上,它会首先评估行条件,然后当它有匹配时在写入之前获取锁定? – Litehouse 2012-08-04 00:47:24