请帮助我理解 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_COMMITTED
和 SELECT ... 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 fromrooms
until Thread 1 is done. Is that correct?
这取决于您的数据库系统使用的并发控制。
MyISAM
会在查询期间锁定整个表。在
SQL Server
中,SELECT
查询在他们检查过的记录/页面/表上放置共享锁,而DML
查询放置更新锁(后来升级为独占锁或降级为共享锁)。独占锁与共享锁不兼容,因此SELECT
或DELETE
查询将锁定直到另一个 session 提交。在使用
MVCC
的数据库中(如Oracle
、PostgreSQL
、MySQL
和InnoDB
),DML
查询创建记录的副本(以一种或另一种方式),通常读取器不会阻止写入器,反之亦然。对于这些数据库,SELECT FOR UPDATE
会派上用场:它会锁定SELECT
或DELETE
查询,直到另一个 session 提交,就像SQL Server
可以。
MySQL
(以及其他几个旧系统)中的 When should one use
REPEATABLE_READ
transaction isolation versusREAD_COMMITTED
withSELECT ... FOR UPDATE
?
一般来说,REPEATABLE READ
不禁止幻像行(行在另一个事务中出现或消失,而不是被修改)
在
Oracle
和更早的PostgreSQL
版本中,REPEATABLE READ
实际上是SERIALIZABLE
的同义词。基本上,这意味着事务在开始后看不到所做的更改。因此,在此设置中,最后一个Thread 1
查询将返回房间,就好像它从未被删除一样(这可能是也可能不是您想要的)。如果您不想在房间被删除后显示房间,您应该使用SELECT FOR UPDATE
锁定行
在
InnoDB
中,REPEATABLE READ
和SERIALIZABLE
是不同的东西:SERIALIZABLE
模式下的 reader他们评估的记录上的 next-key 锁定,有效地防止它们上的并发DML
。因此,在可序列化模式下您不需要SELECT FOR UPDATE
,但在REPEATABLE READ
或READ COMMITED
中确实需要它们。
请注意,隔离模式的标准确实规定您在查询中看不到某些怪癖,但没有定义如何(使用锁定或使用 MVCC
或其他方式)。
当我说“你不需要 SELECT FOR UPDATE
”时,我真的应该添加“因为某些数据库引擎实现的副作用”。
关于mysql - 何时使用 SELECT ... FOR UPDATE?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10935850/