为什么说mysql 的 FOR UPDATE 必须提交了事务才可以被其他人看见?

在数据库中,SELECT ... FOR UPDATE语句用于获取行级锁,它能够确保在当前事务持有锁的情况下,其他事务无法读取或修改被锁定的行。要理解这一点,先要了解数据库事务和锁的工作机制。

事务隔离级别与行级锁

数据库的事务隔离级别定义了一个事务与其他事务之间的可见性和相互影响。常见的隔离级别包括:

  1. Read Uncommitted(读未提交):一个事务可以读取另一个未提交事务的更改,容易导致脏读。
  2. Read Committed(读已提交):一个事务只能读取已经提交的事务所做的更改,防止脏读。
  3. Repeatable Read(可重复读):一个事务可以多次读取同一行数据,并且在这个事务期间,不允许其他事务修改该数据,从而防止不可重复读和脏读。
  4. Serializable(可串行化):事务完全隔离,仿佛它们是一个接一个地顺序执行,避免幻读。

SELECT ... FOR UPDATE通常在Read Committed或更高的隔离级别中使用,并且会在事务期间对查询的行加上排他锁(Exclusive Lock)。

FOR UPDATE如何工作?

当执行SELECT ... FOR UPDATE时,数据库会对选中的行加上排他锁。这意味着:

  1. 锁的作用范围:只有当前事务可以读取或修改这些行,其他事务在试图读取或修改这些行时会被阻塞,直到当前事务结束(提交或回滚)。
  2. 事务提交前的状态:在当前事务提交之前,其他事务无法看到这些行的变化,因为锁会阻止其他事务对这些行的访问。
  3. 事务提交后的状态:一旦当前事务提交,锁会被释放,其他事务就可以读取这些行的新状态,并且可能对其进行修改。

示例分析

假设有两个事务 T1T2,它们都尝试更新同一行数据。

  • 事务 T1 执行 SELECT ... FOR UPDATE,数据库锁定了该行。
  • 事务 T2 尝试执行同样的查询,但由于行已被锁定,T2 会被阻塞,直到 T1 结束(提交或回滚)。
-- T1: 开始事务
BEGIN;

-- T1: 锁定 id 为 1 的账户行
SELECT * FROM account WHERE id = 1 FOR UPDATE;

-- T2: 开始事务
BEGIN;

-- T2: 尝试锁定相同的行,因 T1 持有锁而被阻塞
SELECT * FROM account WHERE id = 1 FOR UPDATE;

-- T1: 提交事务,释放锁
COMMIT;

-- T2: 现在可以锁定并操作该行

事务提交的重要性

  1. 锁的生命周期:锁的存在时间是从SELECT ... FOR UPDATE执行时开始,到事务提交或回滚时结束。在锁存续期间,其他事务无法获取该行的锁,因此也无法读取或修改。
  2. 提交后的可见性:一旦事务提交,锁被释放,其他事务可以立即读取或修改该行数据,并且可以看到提交后的最新状态。

为什么必须提交事务后,其他事务才能看到更改?

事务未提交时,任何操作仅对当前事务可见。未提交的事务可能会回滚(撤销更改),因此数据库需要保持数据的一致性。在FOR UPDATE场景中,未提交事务锁定的数据对其他事务不可见,防止其他事务读取到不完整或不一致的数据。

总结

SELECT ... FOR UPDATE语句通过在行上加锁,确保数据的并发访问是串行的。事务提交之前,其他事务无法看到或修改被锁定的行数据。这种机制有效地避免了并发引发的数据一致性问题,同时保证了事务隔离级别的正确性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值