2. 乐观锁与悲观锁
Q: MySQL乐观锁和悲观锁是什么?
悲观锁(Pessimistic Lock)
基本思想
悲观锁认为被它保护的数据是极其不安全的,每时每刻都有可能被改动,一个事务拿到悲观锁后,其他任何事务都不能对该数据进行修改,只能等待锁被释放才可以执行。
实现方式
数据库中的行锁、表锁、读锁、写锁均为悲观锁。
-- 悲观锁示例:使用 SELECT ... FOR UPDATE
BEGIN;
SELECT * FROM account WHERE id = 1 FOR UPDATE; -- 加排他锁
UPDATE account SET balance = balance - 100 WHERE id = 1;
COMMIT;乐观锁(Optimistic Lock)
基本思想
乐观锁认为数据的变动不会太频繁。
实现方式
乐观锁通常是通过在表中增加一个**版本(version)或时间戳(timestamp)**来实现,其中,版本最为常用。
工作流程
- 事务在从数据库中取数据时,会将该数据的版本也取出来(v1)
- 当事务对数据变动完毕想要将其更新到表中时,会将之前取出的版本v1与数据中最新的版本v2相对比
- 如果v1=v2,那么说明在数据变动期间,没有其他事务对数据进行修改,此时,就允许事务对表中的数据进行修改,并且修改时version会加1,以此来表明数据已被变动
- 如果v1不等于v2,那么说明数据变动期间,数据被其他事务改动了,此时不允许数据更新到表中,一般的处理办法是通知用户让其重新操作
-- 乐观锁示例:使用版本号
-- 1. 查询时获取版本号
SELECT id, balance, version FROM account WHERE id = 1;
-- 假设查到:id=1, balance=1000, version=5
-- 2. 更新时检查版本号
UPDATE account
SET balance = balance - 100, version = version + 1
WHERE id = 1 AND version = 5;
-- 3. 检查影响行数
-- 如果影响行数为0,说明版本号已变化,更新失败,需要重试Q: 乐观锁和悲观锁的适用场景?
悲观锁适用场景
- 写操作频繁:数据冲突概率高的场景
- 对数据一致性要求极高:不能容忍任何数据不一致
- 短事务:锁持有时间短,不会长时间阻塞其他事务
乐观锁适用场景
- 读操作频繁:数据冲突概率低的场景
- 可以容忍重试:冲突时可以重新尝试操作
- 长事务:避免长时间持有锁
性能对比
| 对比项 | 悲观锁 | 乐观锁 |
|---|---|---|
| 冲突处理 | 阻塞等待 | 重试机制 |
| 性能 | 冲突频繁时性能好 | 冲突少时性能好 |
| 实现复杂度 | 简单(数据库支持) | 复杂(需要应用层实现) |
| 死锁风险 | 有 | 无 |
| 适用场景 | 写多读少 | 读多写少 |
注意:不同于悲观锁,乐观锁通常是由开发者在应用层实现的,数据库本身不直接提供乐观锁机制。