mysql 中的乐观锁和悲观锁是两种不同的并发控制策略,它们各有特点和适用场景。
一、悲观锁(pessimistic locking)
概念
悲观锁假设数据冲突经常发生,因此在操作数据之前先获取锁,确保在事务期间数据不会被其他事务修改。
实现方式
使用数据库内置的锁机制:
-- 1. 开始事务 start transaction; -- 2. 加排他锁(悲观锁) select * from products where id = 1 for update; -- 3. 执行业务逻辑 update products set stock = stock - 1 where id = 1; -- 4. 提交事务 commit;
特点
- 思想:先加锁,后操作
- 实现:依赖数据库锁机制(行锁、表锁等)
- 适用场景:
- 写操作频繁的场景
- 数据竞争激烈的环境
- 需要强一致性的业务
优缺点
优点:
- 保证强一致性
- 实现简单,直接使用数据库功能
- 适合高并发写场景
缺点:
- 性能开销大(加锁、释放锁)
- 可能产生死锁
- 降低系统并发度
二、乐观锁(optimistic locking)
概念
乐观锁假设数据冲突很少发生,因此不加锁直接操作,只在提交时检查数据是否被修改过。
实现方式
通常使用版本号或时间戳:
方法1:版本号机制
-- 表结构增加版本字段
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 = 1; -- 这里的1是读取时的版本号
-- 检查影响行数,如果为0表示版本不一致,需要重试
方法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
where id = 1 and update_time = '2024-01-01 10:00:00';
方法3:条件判断(基于业务字段)
-- 基于库存数量判断 update products set stock = stock - 1 where id = 1 and stock > 0;
重试机制
由于乐观锁可能失败,通常需要配合重试逻辑:
// java 示例代码
public boolean deductstockwithretry(int productid, int quantity) {
for (int i = 0; i < max_retry; i++) {
product product = productdao.getbyid(productid);
if (product.getstock() < quantity) {
return false; // 库存不足
}
int affectedrows = productdao.updatestock(productid, quantity, product.getversion());
if (affectedrows > 0) {
return true; // 更新成功
}
// 版本冲突,重试
thread.sleep(100); // 短暂等待
}
throw new runtimeexception("更新失败,超过最大重试次数");
}特点
- 思想:先操作,提交时检查
- 实现:基于应用层逻辑(版本号、时间戳等)
- 适用场景:
- 读多写少的场景
- 数据冲突较少的业务
- 对性能要求高的系统
优缺点
优点:
- 提高系统并发性能
- 避免死锁问题
- 减少数据库锁开销
缺点:
- 实现相对复杂
- 需要处理更新失败的情况
- 不适合高并发写场景
三、对比总结
| 特性 | 悲观锁 | 乐观锁 |
|---|---|---|
| 思想 | 先加锁后操作 | 先操作后检查 |
| 实现方式 | 数据库锁机制 | 版本号/时间戳 |
| 性能 | 并发度低,开销大 | 并发度高,开销小 |
| 适用场景 | 写多读少,冲突频繁 | 读多写少,冲突较少 |
| 一致性 | 强一致性 | 最终一致性 |
| 死锁风险 | 有 | 无 |
| 实现复杂度 | 简单 | 相对复杂 |
四、实际应用建议
选择悲观锁的场景
- 银行转账、库存扣减等金融业务
- 需要强一致性的核心业务
- 写操作非常频繁的系统
选择乐观锁的场景
- 商品浏览、社交媒体的点赞功能
- 读多写少的业务系统
- 对性能要求较高的互联网应用
混合使用策略
在实际项目中,可以根据不同业务场景混合使用:
-- 核心业务使用悲观锁 start transaction; select * from account where user_id = 123 for update; -- 执行资金操作 commit; -- 非核心业务使用乐观锁 update article set like_count = like_count + 1 where id = 456 and version = current_version;
五、注意事项
- 悲观锁要合理设置事务隔离级别和锁等待超时时间
- 乐观锁要设计合理的重试策略和重试次数上限
- 在高并发场景下,乐观锁的重试可能造成"惊群效应"
- 根据业务特点选择合适的锁策略,不要一概而论
这两种锁策略各有优劣,关键在于根据具体的业务场景和性能要求来选择最合适的方案。
到此这篇关于mysql乐观锁和悲观锁的使用的文章就介绍到这了,更多相关mysql乐观锁和悲观锁内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论