mysql - 何时使用 SELECT ... FOR UPDATE?

标签 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;
    • 返回 [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.room_id = 1 AND tags.id = room_tags.tag_id;
    • 返回一个空列表

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

问题 2:什么时候应该使用 SERIALIZABLE 事务隔离与 READ_COMMITTEDSELECT ... FOR UPDATE ?

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

最佳答案

实现房间和标签之间的一致性并确保房间在被删除后永远不会返回的唯一可移植方法是使用 SELECT FOR UPDATE 锁定它们。

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


To solve this problem, Thread 1 should SELECT id FROM rooms FOR UPDATE, thereby preventing Thread 2 from deleting from rooms until Thread 1 is done. Is that correct?

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

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

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

  • 在使用 MVCC 的数据库中(如 OraclePostgreSQLMySQL InnoDB),DML 查询创建记录的副本(以一种或另一种方式),通常读取器不会阻止写入器,反之亦然。对于这些数据库,SELECT FOR UPDATE 会派上用场:它会锁定 SELECTDELETE 查询,直到另一个 session 提交,就像 SQL Server 可以。

When should one use REPEATABLE_READ transaction isolation versus READ_COMMITTED with SELECT ... FOR UPDATE?

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

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

  • 锁定行
  • InnoDB 中,REPEATABLE READSERIALIZABLE 是不同的东西:SERIALIZABLE 模式下的 reader他们评估的记录上的 next-key 锁定,有效地防止它们上的并发 DML。因此,在可序列化模式下您不需要 SELECT FOR UPDATE,但在 REPEATABLE READREAD COMMITED 中确实需要它们。

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

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

关于mysql - 何时使用 SELECT ... FOR UPDATE?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10935850/

相关文章:

PHP 在列名称中使用连字符获取 pdo

javascript - Node.js 解析错误与 mysql 查询

sql-server - 如何获取以下场景的 CSV 值

sql-server - 将 DateTime 转换为 nvarchar,仅月份和年份

MySQL 连接器 6.7.4 Entity Framework 5 模型优先

mysql - 这是 WooCommerce/WordPress 数据库中的订单和购买用户之间的关联

mysql - 通过php连接xcode到mysql

sql - PostgreSQL group by with interval 但没有窗口函数

sql - 如何用想要的结果做这个 GROUP BY?

sql - 德尔福7 : Get SQL server system Date and Time format