何时使用SELECT ... FOR UPDATE?

时间:2012-06-07 16:23:06

标签: mysql sql sql-server transactions select-for-update

请帮助我了解SELECT ... FOR UPDATE背后的用例。

问题1 :以下是应该使用SELECT ... FOR UPDATE的一个很好的例子吗?

假设:

  • 房间[ID]
  • 标签[id,name]
  • room_tags [room_id,tag_id]
    • room_id和tag_id是外键

应用程序想要列出所有房间及其标签,但需要区分没有标签的房间与已移除的房间。如果未使用SELECT ... FOR UPDATE,可能发生的情况是:

  • 最初:
    • 会议室包含[id = 1]
    • 标签包含[id = 1, name = 'cats']
    • room_tags包含[room_id = 1, tag_id = 1]
  • 主题1:SELECT id FROM rooms;
    • returns [id = 1]
  • 主题2:DELETE FROM room_tags WHERE room_id = 1;
  • 主题2:DELETE FROM rooms WHERE id = 1;
  • 主题2:[提交交易]
  • 主题1:SELECT tags.name FROM room_tags, tags WHERE room_tags.tag_id = 1 AND tags.id = room_tags.tag_id;
    • 返回一个空列表

现在,线程1认为房间1没有标签,但实际上房间已被移除。要解决此问题,线程1应SELECT id FROM rooms FOR UPDATE,从而阻止线程2从rooms删除,直到线程1完成。这是对的吗?

问题2 :应该何时使用SERIALIZABLE交易隔离与READ_COMMITTED进行SELECT ... FOR UPDATE

答案应该是可移植的(不是特定于数据库的)。如果那是不可能的,请解释原因。

2 个答案:

答案 0 :(得分:66)

实现房间和标签之间一致性以及确保房间在删除后永远不会返回的唯一便携方式是使用SELECT FOR UPDATE锁定它们。

但是在某些系统中,锁定是并发控制的副作用,您可以在不明确指定FOR UPDATE的情况下获得相同的结果。


  

要解决此问题,线程1应SELECT id FROM rooms FOR UPDATE,从而阻止线程2从rooms删除,直到线程1完成。这是对的吗?

这取决于数据库系统正在使用的并发控制。

  • MyISAM MySQL(以及其他几个旧系统)确实会在查询期间锁定整个表格。

  • SQL Server中,SELECT查询在他们检查过的记录/页面/表上放置共享锁,而DML查询放置更新锁(后来被提升为独占)或降级为共享锁)。独占锁与共享锁不兼容,因此SELECTDELETE查询将锁定,直到另一个会话提交。

  • 在使用MVCC(例如OraclePostgreSQLMySQLInnoDB)的数据库中,DML查询创建记录的副本(以一种或另一种方式)并且通常读者不会阻止写入者,反之亦然。对于这些数据库,SELECT FOR UPDATE会很方便:它会锁定SELECTDELETE查询,直到另一个会话提交,就像SQL Server一样。

    < / LI>
  

何时应该使用REPEATABLE_READ交易隔离与READ_COMMITTED SELECT ... FOR UPDATE进行隔离?

通常,REPEATABLE READ不禁止幻像行(在另一个事务中出现或消失的行,而不是被修改的行)

  • Oracle及之前的PostgreSQL版本中,REPEATABLE READ实际上是SERIALIZABLE的同义词。基本上,这意味着事务在启动后看不到更改。因此,在此设置中,最后一个Thread 1查询将返回房间,就好像它从未被删除一样(可能是也可能不是您想要的)。如果您不想在删除后显示房间,则应使用SELECT FOR UPDATE

  • 锁定行
  • InnoDB中,REPEATABLE READSERIALIZABLE是不同的事情:SERIALIZABLE模式下的读者在他们评估的记录上设置了下一键锁,有效地阻止了并发DML就可以了。因此,您在序列化模式下不需要SELECT FOR UPDATE,但在REPEATABLE READREAD COMMITED中确实需要它们。

请注意,隔离模式的标准确实规定您在查询中没有看到某些怪癖,但没有定义如何(使用锁定或使用MVCC或其他方式)。

当我说“你不需要SELECT FOR UPDATE”时,我真的应该添加“因为某些数据库引擎实现的副作用”。

答案 1 :(得分:21)

简答:

Q1:是的。

Q2:你使用哪个并不重要。

答案很长:

select ... for update将(如其所暗示)选择某些行,但也将它们锁定,就像它们已被当前事务更新一样(或者就像已经执行了身份更新一样)。这允许您在当前事务中再次更新它们,然后提交,而无需其他事务以任何方式修改这些行。

另一种看待它的方式,就好像以下两个语句是以原子方式执行的:

select * from my_table where my_condition;

update my_table set my_column = my_column where my_condition;

由于受my_condition影响的行被锁定,其他任何事务都无法以任何方式修改它们,因此,事务隔离级别在这里没有区别。

另请注意,事务隔离级别与锁定无关:设置不同的隔离级别不允许您绕过事务锁定的其他事务中的锁定和更新行。

什么事务隔离级别保证(在不同级别)是事务正在进行时数据的一致性。