☰
乐观锁(MyBatis-Plus)
2026/10/14 17:32:18 网站建设 项目流程

乐观锁(MyBatis-Plus)

    • ✅ 一句话定义
    • ✅ 实现原理(版本号机制,MP默认)
    • ✅ MyBatis-Plus 使用3步
    • ✅ MP乐观锁的局限(重点)
    • ✅ 乐观锁其他实现方案
    • ✅ 适用场景&不适用场景
    • ✅ 乐观锁 vs 悲观锁
    • ✅ 常见坑点
    • ✅ 面试一句话
    • ✅

✅ 一句话定义

乐观锁是并发控制方案,思想:乐观地认为数据很少发生并发冲突,读取数据不加锁;更新的时候校验版本号,判断数据有没有被别人修改过。如果版本不一致,更新失败,防止并发更新覆盖数据。

场景:多个人同时修改同一条记录,避免更新丢失(后提交的人把前面人的修改覆盖掉)

✅ 实现原理(版本号机制,MP默认)

  1. 数据库表增加version数字字段,默认值=1
  2. 查询数据时,同时读出当前version
  3. 更新时:SQL 自动带上条件where id=? and version=?;更新成功,version +1
  4. 如果别人已经抢先修改,数据库里version已经变大,条件不成立,更新行数为0,修改失败

自动生成SQL示例:
update user set name="xxx",version=2 where id=1 and version=1

✅ MyBatis-Plus 使用3步

  1. 数据库表添加version int default 1
  2. 实体类字段上加@Version
@Version private Integer version;
  1. 配置类注册乐观锁插件OptimisticLockerInnerInterceptor
@Configuration public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); //乐观锁插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//分页插件 return interceptor; } }

✅ MP乐观锁的局限(重点)

  1. 仅在特定更新方式生效
    ✅ 生效:updateById(实体对象)、update(实体对象, wrapper)
    ❌ 不生效:只传Wrapper,不带实体对象的更新,MP不会自动携带version、不会自动+1版本号
// ✅ 有效,自动维护version User user = userMapper.selectById(1L); user.setName("新名字"); userMapper.updateById(user); // ❌ 无效!没有实体,不会处理version UpdateWrapper<User> wrapper = new UpdateWrapper<>(); wrapper.set("name","新名字").eq("id",1L); userMapper.update(null, wrapper);
  1. 一次更新只能识别一次冲突:如果并发非常激烈,乐观锁更新失败,需要代码手动重试机制,框架不会自动重试。
  2. 只能防止更新丢失,不能解决脏读:乐观锁无法阻止事务读取到未提交的数据,它只控制提交更新时的覆盖问题。

✅ 乐观锁其他实现方案

MP默认是版本号(version);还有另一种常用方案:时间戳timestamp
原理:每次更新时校验读取时的时间戳,更新成功刷新时间戳。
缺点:时间戳依赖服务器时间,集群环境时间不同步会产生问题,所以项目优先使用version数字版本。

✅ 适用场景&不适用场景

✅ 适合(读多写少):商品信息修改、用户资料编辑、文档修改,并发冲突概率低,不想用数据库锁降低性能。
❌ 不适合(写多读少,频繁并发争抢):库存扣减、秒杀,冲突概率极高,大量更新失败不断重试,性能很差,这种场景推荐悲观锁/分布式锁。

✅ 乐观锁 vs 悲观锁

乐观锁悲观锁
锁位置业务层版本号,不占用数据库锁数据库行锁,查询时锁住记录
并发性能高,读操作无阻塞低,写操作会阻塞其他请求
冲突处理更新失败,业务重试/提示用户其他事务阻塞等待锁释放
数据库支持数据库无关,纯业务逻辑依赖数据库引擎(InnoDB支持行锁)
典型实现MP @Version版本号select … for update

✅ 常见坑点

  1. version字段类型:推荐Integer/Long,不要用tinyint,版本号不断增长容易溢出。
  2. 新增数据:必须给version设置默认值1,数据库字段version int default 1,否则新增数据version为null,后续更新校验失效。
  3. 多表关联更新:MP乐观锁插件不支持多表SQL,手写多表更新SQL不会自动维护版本号。

✅ 面试一句话

乐观锁是一种并发控制策略,假设并发冲突很少发生,查询不加锁,在提交更新时校验版本号判断数据是否被修改。MyBatis-Plus通过@Version注解+乐观锁插件实现,自动拼接version条件并递增版本;但它只对带实体对象的update方法生效,高并发写场景下大量失败,适合读多写少业务。

✅

乐观锁是解决并发更新丢失问题的方案,核心思想认为同一数据很少会同时被多人修改,读取数据时不加锁,更新的时候校验版本号。

在MyBatis-Plus中使用乐观锁,需要在数据表增加version字段,实体类使用@Version标记版本属性,并且在配置类注册乐观锁拦截器。当执行更新时,MP会自动在where条件带上当前版本号,更新成功则版本号+1;如果版本号不匹配,说明数据已经被其他用户修改,本次更新失败。

⚠️ 注意:MP乐观锁仅在传入实体对象的update方法生效。纯Wrapper更新不会自动维护版本号。乐观锁适合读多写少场景;如果是高并发争抢数据场景,不推荐乐观锁。


需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询