在这些情况下,sql中是否会出现竞争条件?
如果我在一个线程中运行此SQL更新,则调用它语句1:
更新项目 设置标志= B. 其中Flag = A;
这个在另一个中运行的SQL更新调用它语句2:
更新项目 设置标志= C. 其中Flag = A;
每个线程是否可以读取Flag等于A的相同记录并用自己的值写入记录?这样声明1可以先写它然后声明2写它或反之亦然?
此问题的答案取决于数据库何时独占锁定更新。它是在找到记录之前还是在找到记录并评估where子句之后发生的?
答案 0 :(得分:5)
首先,有三种锁定上下文:
然后你有四种锁定模式:
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使用称为“间隙”锁的特殊锁来确保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锁定并最终执行但不匹配任何行。
答案 1 :(得分:0)
在这种情况下,将使用用于数据修改操作的独占锁,例如INSERT,UPDATE或DELETE。
独占锁可确保无法同时对同一资源进行多次更新。
在这种情况下,您不会遇到竞争条件。
如果您有一个涉及多个表的更复杂的场景,那么您可能会遇到竞争条件或死锁。有许多方法可以避免这种情况,简化和分离查询等。
您还可以将提示应用于告诉SQL使用何种锁类型的查询。
http://msdn.microsoft.com/en-us/library/aa213026(v=sql.80).aspx
答案 2 :(得分:0)
听起来你应该读一下锁定。 SQL Server具有一组复杂的逻辑,并将根据其估计需要更新的行数执行表级或行级锁。除非您明确告诉它您希望它执行哪个,否则它甚至可能因查询而异。通常,如果要修改表的一小部分,它将选择行级锁定。
SQL Server在设计时考虑了ACID,因此它在对数据执行任何实际更新之前将更改写入其日志。这样可以回滚任何失败的更新,并允许查询之间的一致性(就像您的询问一样)。您可以执行脏读以解决锁定问题,但是无法阻止SQL Server锁定插入,更新和/或删除的记录。
编辑:这是一篇关于ACID的文章。 ACID - Wikipedia
答案 3 :(得分:0)
所有SQL数据库都可以保证不会发生这种冲突。 “何时”发生锁定取决于锁定是在表,分区,页面还是行级别。或者,您是否已在数据库中关闭此类锁定。
如果您有并发更新语句并且正在更新多行,会发生什么情况,第一行更新第一行,第二行更新。
一般来说,我认为where子句被评估为选择行集,一次锁定一行,执行更新和解锁。但是,这取决于锁定的类型。在这种情况下,上面的场景将继续值翻转。
如果您担心这种情况,请在处理并发更新请求时使用表级锁定强制序列化。