MySQL中的乐观锁和悲观锁的区别及说明
目录
- 1. 悲观锁(Pessimistic Locking)
- 核心思想
- 实现方式
- 适用场景
- 示例
- 2. 乐观锁(Optimistic Locking)
- 核心思想
- 实现方式
- 适用场景
- 示例
- 3. 对比总结
- 4. mysql 中的实际选择
- 总结
在 MySQL 中,乐观锁和悲观锁是两种不同的并发控制机制,用于解决多用户/事务同时操作数据时的冲突问题。
它们的核心理念和实现方式有显著区别:
1. 悲观锁(Pessimistic Locking)
核心思想
假设数据会被频繁修改,因此提前对数据进行加锁,防止其他事务访问,直到当前事务完成操作并释放锁。
实现方式
php显式加锁:通过 SQL 语句主动申请锁。
- 排他锁(X Lock):
SELECT ... FOR UPDATE
(在事务中锁定选中的行,阻止其他事务修改或加锁)
- 共享锁(S Lock):
SELECT ... LOCK IN SHARE MODE
(允许其他事务读,但阻止写操作)
隐式加锁:数据库自动管理(如 MySQL 的 InnoDB 引擎默认使用行级锁)。
适用场景
- 写多读少的场景(如频繁更新的数据)。
- 需要强一致性且冲突概率较高时(如金融交易)。
示例
START TRANSACTION; -- 锁定用户账户余额(排他锁) SELECT balance FROM accounts WHERE user_id = 1 FOR UPDATE; -- 执行扣款操作 UPDATE accounts SET balance = balance - 100 WHERE user_id = 1; COMMIT;
优点:
- 保证数据操作的原子性和一致性。
- 适合高竞争场景(冲突概率高时效率更高)。
缺点:
- 可能导致死锁(需应用层处理)。
- 降低并发性能(长时间持有锁会阻塞其他操作)。
2. 乐观锁(Optimistic Locking)
核心思想
假设数据冲突较少发生,因此不加锁,而是在更新时检查数据是否被修改过。若被修改过,则编程拒绝操作或重试。
实现方式
- 版本号(Version):在表中增加
version
字段,更新时验证版本号。
UPDATE table SET column = new_value, version = version + 1 WHERE id = 1 AND version = old_version;
- 时间戳(Timestamp):类似版本号,但使用时间戳标记数据修改时间。
- CAS(Compare-And-Swap):在应用层比较数据一致cVlpRvI性后再提交。
适用场景
- 读多写少的场景(如商品库存充足时的秒杀)。
- 冲突概率较低时(如用户点赞操作)。
示例
-- 初始查询(获取当前版本号) SELECT balance, version FROM accounts WHERE user_id = 1; -- 更新时检查版本号 UPDATE accounts SET balance = balance - 100, version = version + 1 WHERE user_id = 1 AND version = 1; -- 假设旧版本号是1 -- 如果受影响行数=0,说明版本已过期,需重试或报错
优点:
- 无锁竞争,并发性能高。
- 避免死锁问题。
缺点:
- 冲突发生时需处理重试逻辑(如循环重试或返回错误)。
- 无法保证强一致性(最终一致性)。
3. 对比总结
特性 | 悲观锁 | 乐观锁 |
---|---|---|
加锁时机 | 操作前加锁 | 操作后验证 |
实现复杂度 | 依赖数据库机制 | 需应用层配合(如版本号) |
并发性能 | 较低(锁竞争) | 较高(无锁) |
适用场景 | 高竞争、强一致性 | 低竞争、最终android一致性 |
典型问题 | 死锁、性能瓶颈 | 版本冲突、重试逻辑 |
4. MySQL 中的实际选择
- InnoDB 引擎:支持行级锁,适合悲观锁(需显式使用
FOR UPDATE
)。 - MyISAM 引擎:仅支持表级锁,悲观锁性能较差,通常不推荐。
- 乐观锁:需在应用层实现(如通过版本号字段),与存储引擎无关。
根据业务场景选择:
- 金融交易等强一致性android场景 → 悲观锁
- 高并发读多写少场景 → 乐观锁
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。
精彩评论