我试图弄清楚这是否是一个隔离问题,以及我如何解决它。假设我有一个包含2个并发连接的数据库:
0. BEGIN transaction TX1
1. TX1 performs SELECT VALUE 10
2. TX1 performs INSERT VALUE 10 since it does not exist
3. TX1 performs SELECT VALUE 20
4. TX1 performs INSERT VALUE 20 since it does not exist
5. BEGIN transaction TX2 in a different session, using a different connection
6. TX2 performs SELECT VALUE 10
7. TX2 performs INSERT VALUE 10 since TX1 hasn't committed yet
8. COMMIT TX2
9. COMMIT TX1
当前的隔离级别是可重复读取的。我可以通过更改为不同的隔离级别来解决此问题吗?如果是,那会是什么?读未提交?
答案 0 :(得分:2)
我试着将我的评论作为更长的解释来制定。前提条件:InnoDB和VALUE具有唯一的密钥或主键。
从OPs示例开始
0. BEGIN transaction TX1
1. TX1 performs SELECT VALUE 10
2. TX1 performs INSERT VALUE 10 since it does not exist
3. TX1 performs SELECT VALUE 20
4. TX1 performs INSERT VALUE 20 since it does not exist
5. BEGIN transaction TX2 in a different session, using a different connection
6. TX2 performs SELECT VALUE 10
7. TX2 performs INSERT VALUE 10 since TX1 hasn't committed yet
TX2 will block here until TX1 finishes, if TX1 rolls back this step succeedes,
if TX1 commits, you get an error here.
要防止出现这种情况,请使用FOR UPDATE
查询的SELECT
后缀:
0. BEGIN transaction TX1
1. TX1 performs SELECT VALUE 10 FOR UPDATE
2. TX1 performs INSERT VALUE 10 since it does not exist
3. TX1 performs SELECT VALUE 20 FOR UPDATE
4. TX1 performs INSERT VALUE 20 since it does not exist
5. BEGIN transaction TX2 in a different session, using a different connection
6. TX2 performs SELECT VALUE 10 FOR UPDATE
based on the `FOR UPDATE` condition, TX2 will block here until TX1 finishes.
If TX1 commits, the result will be the row TX1 committed, if TX1 rolls back,
this query returns an empty result.
通过使用FOR UPDATE
关键字,您可以避免TX2失败并始终使用最新数据 - 它会导致创建行锁(因此请谨慎使用)。但是,请注意死锁(Database Deadlock in SELECT FOR UPDATE;始终以相同的顺序“锁定”)