在数据库并发控制中,乐观锁和悲观锁是两种核心的并发控制策略,它们分别适用于不同的业务场景。本文将详细介绍这两种锁机制在MySQL中的实现方式、适用场景以及实际应用案例。
一、悲观锁:先加锁再操作
1. 核心思想
悲观锁认为并发冲突随时可能发生,因此在访问数据前先对数据加锁,阻止其他事务修改数据,直到当前事务完成操作后才释放锁。
2. 实现方式
在MySQL中,悲观锁主要通过以下两种语句实现:
排他锁(Exclusive Lock)
START TRANSACTION;
SELECT * FROM products WHERE id = 1 FOR UPDATE;
-- 执行业务逻辑
UPDATE products SET stock = stock - 1 WHERE id = 1;
COMMIT;
共享锁(Shared Lock)
START TRANSACTION;
SELECT * FROM products WHERE id = 1 LOCK IN SHARE MODE;
-- 其他事务可以读取但不能修改
COMMIT;
3. 关键特性
- 锁粒度:InnoDB引擎默认使用行级锁,基于索引实现精准锁定
- 锁释放时机:事务结束时(提交或回滚)自动释放锁
- 事务要求:必须在显式事务中使用,否则锁会立即释放
4. 注意事项
- 必须关闭MySQL的自动提交:
SET autocommit = 0; - 使用索引条件,避免无索引导致表级锁
- 控制事务执行时间,避免长时间持有锁引发死锁
二、乐观锁:先操作再校验
1. 核心思想
乐观锁假设并发冲突很少发生,因此在读取数据时不加锁,只在更新数据时检查数据是否被其他事务修改过。如果数据未被修改,则更新成功;否则更新失败,需要重试或提示用户。
2. 实现方式
版本号机制(最常用)
-- 创建表时添加version字段
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(100),
stock INT,
version INT DEFAULT 0
);
-- 更新数据时检查版本号
UPDATE products
SET stock = stock - 1, version = version + 1
WHERE id = 1 AND version = 2;
时间戳机制
-- 创建表时添加时间戳字段
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(100),
stock INT,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 更新数据时检查时间戳
UPDATE products
SET stock = stock - 1, update_time = NOW()
WHERE id = 1 AND update_time = '2025-01-06 10:00:00';
3. 冲突处理
- 通过
ROW_COUNT()判断更新是否成功 - 更新失败时,可以重试或提示用户数据已被修改
- 建议在应用层实现重试机制
三、两种锁机制对比
| 维度 | 悲观锁 | 乐观锁 |
|---|---|---|
| 核心思想 | 先加锁再操作 | 先操作再校验 |
| 实现方式 | SELECT … FOR UPDATE | 版本号/时间戳机制 |
| 性能开销 | 高(锁竞争、阻塞) | 低(无锁等待) |
| 数据一致性 | 强一致性 | 最终一致性 |
| 适用场景 | 高冲突、写多读少 | 低冲突、读多写少 |
| 典型应用 | 金融转账、库存扣减 | 商品浏览、评论系统 |
四、实际应用场景选择
1. 选择悲观锁的场景
- 金融业务:银行转账、账户余额修改,数据一致性要求极高
- 库存扣减:秒杀系统、商品抢购,避免超卖
- 长事务场景:操作耗时久,需要持续占用资源
2. 选择乐观锁的场景
- 社交应用:用户信息更新、评论发布
- 内容管理系统:文章编辑、点赞功能
- 高并发读场景:商品浏览、新闻阅读
五、最佳实践建议
1. 悲观锁优化
- 按固定顺序加锁,避免死锁
- 使用索引条件,确保行级锁
- 控制事务粒度,减少锁持有时间
2. 乐观锁优化
- 添加重试机制,处理更新失败
- 使用INT类型版本号,避免溢出
- 高并发场景可结合CAS操作
3. 混合使用策略
在实际业务中,可以根据不同场景灵活选择:
- 核心业务使用悲观锁保证强一致性
- 非核心业务使用乐观锁提升并发性能
- 必要时可结合分布式锁解决跨系统并发问题
总结
MySQL的乐观锁和悲观锁各有优劣,选择哪种锁机制取决于具体的业务场景和性能要求。悲观锁通过数据库层面的锁机制保证强一致性,适合高冲突场景;乐观锁通过业务逻辑实现并发控制,适合低冲突、高并发场景。在实际开发中,应根据业务特点合理选择,必要时可以混合使用,以达到最佳的性能和一致性平衡。