悲观锁、乐观锁、mybatis-plus实现乐观锁

转载自:www.javaman.cn

1、悲观锁、乐观锁

乐观锁和悲观锁是两种用于处理并发操作的数据锁定策略。它们在处理多个事务尝试同时访问和修改同一数据时的方法有所不同。

  1. 悲观锁 (Pessimistic Locking)

    • 概念:悲观锁是一种基于悲观态度的数据并发控制机制。它总是假设最坏的情况,即认为其他事务会尝试修改数据,因此在读取数据时就会加锁,以确保在此期间其他事务不能修改数据。
    • 工作原理:当一个事务想要读取或修改数据时,它会先请求锁。如果锁已经被其他事务持有,则当前事务会被阻塞,直到锁被释放。这确保了每次只有一个事务可以修改数据。
    • 使用场景:悲观锁适用于写操作较多的场景,或者当数据冲突可能性较高时。它可以在数据库层面实现,如行锁、表锁等。
    • 优点:能够有效防止数据冲突,确保数据的一致性。
    • 缺点:如果锁的粒度过大或持有时间过长,可能导致并发性能下降和资源浪费。
  2. 乐观锁 (Optimistic Locking)
    • 概念:乐观锁是基于乐观态度的数据并发控制机制。它总是假设最好的情况,即在读取数据和提交更新之间,其他事务不会修改数据。因此,它不会在读取数据时加锁,而是在更新数据时检查是否有其他事务已经修改了数据。
    • 工作原理:通常通过版本号、时间戳等机制实现。当事务想要更新数据时,它会检查数据的版本号是否与自己最初读取的版本号相匹配。如果匹配,说明没有其他事务修改过数据,可以进行更新;否则,更新会被拒绝。
    • 使用场景:乐观锁适用于读操作较多、写操作较少的场景,或者当数据冲突可能性较低时。它通常在应用层面实现。
    • 优点:在高并发场景下可以提高性能,因为它减少了不必要的锁等待时间。
    • 缺点:如果有很多写操作或者数据冲突频繁发生,可能导致大量的重试和回滚,从而降低性能。

实战中使用

  • 悲观锁:在关系型数据库中,如MySQL,可以使用SELECT ... FOR UPDATE语句对选定的行或表加锁。在Java中,可以使用synchronized关键字或ReentrantLock等来实现悲观锁。
  • 乐观锁:可以在应用层面实现,例如在更新数据时检查数据的版本号是否发生变化。在MyBatis-Plus中,可以通过OptimisticLockerInnerInterceptor插件来简化乐观锁的使用。

2、mybatis-plus实现乐观锁

MyBatis-plus本身并不直接实现悲观锁或乐观锁,而是提供了与数据库的交互机制,使得开发者能够在应用中实现这两种锁策略。但是,为什么我们经常会看到 MyBatis 与乐观锁的结合,而与悲观锁的结合较少呢?这主要是因为两者在使用场景和适用性上的差异。

  1. 悲观锁

    • 悲观锁主要在数据库层面实现,例如通过 SQL 语句。
    • 当使用悲观锁时,事务在读取数据时就会加锁,确保在此期间其他事务不能修改数据。这涉及到数据库的并发控制和锁定机制,与 MyBatis 的 ORM 功能关系不大。
    • 由于悲观锁的实现更多地依赖于数据库本身的功能,因此 MyBatis 在这方面并没有太多可以增加的价值。
  2. 乐观锁
    • 乐观锁通常在应用层面实现,需要开发者检查数据的版本号、时间戳等来判断数据是否在读取后被其他事务修改。
    • MyBatis-Plus 提供的 OptimisticLockerInnerInterceptor 插件可以简化这一过程,帮助开发者更容易地在应用中实现乐观锁。
    • 由于乐观锁的实现更多地依赖于应用逻辑,MyBatis 在这方面的插件和工具可以为开发者带来便利。

总结:MyBatis 本身并不防止悲观锁或乐观锁,而是提供了与数据库的交互机制。悲观锁主要依赖于数据库的功能来实现,而乐观锁则可以在应用层面得到 MyBatis-Plus 等工具的帮助。

乐观锁的基础使用

MyBatis-Plus 提供了一个内置的乐观锁插件 OptimisticLockerInnerInterceptor 来帮助简化乐观锁的实现。以下是使用 MyBatis-Plus 实现乐观锁的基本步骤:

​ 1.添加乐观锁插件

在你的 MyBatis-Plus 配置中添加乐观锁插件。

@Configuration
@MapperScan({"com.ds.blog.system.mapper","com.ds.blog.admin.mapper"})
public class MybatisPlusConfig { @Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//乐观锁插件
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}

​ 2.标记乐观锁字段

在你的实体类中,使用 @Version 注解来标记作为乐观锁字段的属性,通常是版本号或时间戳。

public class Entity {
@Version
private Integer version;
// 其他字段和方法
}
  1. 更新操作

    当你尝试更新数据时,MyBatis-Plus 会自动检查乐观锁字段。如果数据的版本号与你最初读取的版本号不匹配,更新会被拒绝,并抛出 OptimisticLockException 异常。
  2. 处理乐观锁异常

    在你的业务代码中,需要捕获并处理 OptimisticLockException 异常。通常的做法是重试更新操作或通知用户数据已被其他事务修改。
  3. 自定义乐观锁逻辑

    如果需要更复杂的乐观锁逻辑,你可以实现自己的乐观锁拦截器,并覆盖默认的行为。

注意:乐观锁策略的成功与否还取决于你的并发控制策略和业务场景。

自定义乐观锁拦截器

具体步骤如下:

​ 1.创建自定义拦截器类

首先,你需要创建一个类来实现 MyBatis-Plus 的 InnerInterceptor 接口。这个接口定义了一些方法,允许你在 SQL 执行的不同阶段插入自定义逻辑。

import com.baomidou.mybatisplus.core.parser.SqlInfo;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*; import java.util.Properties; @Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class CustomOptimisticLockInterceptor implements InnerInterceptor {
// 你的自定义逻辑将在这里实现
}

​ 2.实现拦截方法

在你的自定义拦截器类中,你需要实现 processBeforeprocessAfter 方法。这些方法在 SQL 执行之前和之后被调用,允许你插入自定义逻辑。在这个例子中,我们关注 processBefore 方法,因为它允许我们在更新操作之前检查乐观锁。

@Override
public void processBefore(Executor executor, MappedStatement ms, Object parameter, String sql, SqlInfo sqlInfo) {
// 获取当前事务尝试更新的实体对象
Entity entity = (Entity) parameter;
// 获取原始版本号(通常从数据库中读取)
int originalVersion = entity.getVersion();
// 检查版本号是否已经被修改(意味着有其他事务已经更新了这条记录)
if (originalVersion != entity.getVersion()) {
throw new OptimisticLockException("数据已被其他事务修改");
}
// 如果需要检查其他字段或执行其他逻辑,可以在这里添加代码
}

​ 3.注册自定义拦截器

在你的 MyBatis-Plus 配置中,你需要注册你的自定义拦截器,以便它能够被正确地应用到你的 SQL 执行过程中。

@Configuration
@MapperScan({"com.ds.blog.system.mapper","com.ds.blog.admin.mapper"})
public class MybatisPlusConfig { @Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new CustomOptimisticLockInterceptor());
return mybatisPlusInterceptor;
}

悲观锁、乐观锁、mybatis-plus实现乐观锁的更多相关文章

  1. mybatis 如何使用乐观锁

    悲观锁的问题: 因为悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性.如果加锁的时间过长,其他用户长时间无法访问,影响了程序的并发访问性,同时这样对数据库性能开销影响也很大,特别是 ...

  2. 对mysql乐观锁、悲观锁、共享锁、排它锁、行锁、表锁概念的理解

    乐观锁 乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了. 实现: 通常实现是 ...

  3. MySQL/InnoDB中,乐观锁、悲观锁、共享锁、排它锁、行锁、表锁、死锁概念的理解

    文章出处:https://www.souyunku.com/2018/07/30/mysql/?utm_source=tuicool&utm_medium=referral MySQL/Inn ...

  4. Redis专题(3):锁的基本概念到Redis分布式锁实现

    拓展阅读:Redis闲谈(1):构建知识图谱 Redis专题(2):Redis数据结构底层探秘 近来,分布式的问题被广泛提及,比如分布式事务.分布式框架.ZooKeeper.SpringCloud等等 ...

  5. 你用对锁了吗?浅谈 Java “锁” 事

    每个时代,都不会亏待会学习的人 大家好,我是yes. 本来打算继续写消息队列的东西的,但是最近在带新同事,发现新同事对于锁这方面有一些误解,所以今天就来谈谈"锁"事和 Java 中 ...

  6. Redis 分布式锁,C#通过Redis实现分布式锁(转)

    目录(?)[+] 分布式锁一般有三种实现方式: 可靠性   分布式锁一般有三种实现方式: 1. 数据库乐观锁; 2. 基于Redis的分布式锁; 3. 基于ZooKeeper的分布式锁.本篇博客将介绍 ...

  7. (转)SQL SERVER的锁机制(一)——概述(锁的种类与范围)

    锁定:通俗的讲就是加锁.锁定是 Microsoft SQL Server 数据库引擎用来同步多个用户同时对同一个数据块的访问的一种机制. 定义:当有事务操作时,数据库引擎会要求不同类型的锁定,如相关数 ...

  8. InnoDB这种行锁实现特点意味者:只有通过索引条件检索数据,InnoDB才会使用行级锁,否则,InnoDB将使用表锁!

    InnoDB行锁是通过索引上的索引项来实现的,这一点MySQL与Oracle不同,后者是通过在数据中对相应数据行加锁来实现的. InnoDB这种行锁实现特点意味者:只有通过索引条件检索数据,InnoD ...

  9. mysql READ-COMMITTED 模式下 行锁不会升级到表级锁

    mysql> select sn,id,info from s100 group by id; +-----+------+------+ | sn | id | info | +-----+- ...

  10. (转)Synchronized(对象锁)和Static Synchronized(类锁)的区别

    场景:面试的时候经常用得到! 1 综述 Synchronized和Static Synchronized区别 一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念),一个是全 ...

随机推荐

  1. 美团面试拷打:ConcurrentHashMap 为何不能插入 null?HashMap 为何可以?

    周末的时候,有一位小伙伴提了一些关于 ConcurrentHashMap 的问题,都是他最近面试遇到的.原提问如下: 整个提问看着非常复杂,其实归纳来说就是两个问题: ConcurrentHashMa ...

  2. 《SQL与数据库基础》21. 分库分表(一)

    目录 分库分表(一) 拆分策略 垂直拆分 垂直分库 垂直分表 水平拆分 水平分库 水平分表 技术实现 MyCat概述 概念介绍 环境准备 目录介绍 MyCat入门 配置 分片配置(schema.xml ...

  3. 手写raft(三) 实现日志压缩

    手写raft(三) 实现日志压缩 在上一篇博客中MyRaft实现了日志复制功能,按照计划接下来需要实现日志压缩. 手写raft(一) 实现leader选举 手写raft(二) 实现日志复制 1. 什么 ...

  4. 遗传算法解决航路规划问题(MATLAB)

    遗传算法 文章部分图片和思路来自司守奎,孙兆亮<数学建模算法与应用>第二版 定义:遗传算法是一种基于自然选择原理和自然遗传机制的搜索(寻优)算法,模拟自然界中的声明进化机制,在人工系统中实 ...

  5. KRPano最新官方文档中文版

    KRPano最新官方文档中文版: KRPano作为VR全景开发中常用的工具软件,深受广大开发者喜爱,但由于软件本身是国外软件,因此官方的文档都是以英文为主,对于一些国内不太熟悉英文的开发者来说比较不友 ...

  6. Solution Set -「NOI Online R1」

    NOI-Online-T1-序列 其实这道题是全场最难的-- 我这里给出一种并查集的做法. 首先我们把操作2中的 \(u\) 和 \(v\) 合并 对于操作1我们可以把他转化为操作2来做. 比如我们针 ...

  7. Linux部署项目常用命令(持续更新)

    防火墙配置 # 启动防火墙服务 systemctl start firewalld # 关闭防火墙服务 systemctl stop firewalld # 查看防火墙服务状态 systemctl s ...

  8. Oracle查询--增加--删除--修改主键

    对Oracle表主键的操作,有四类:查询,增加,修改,删除 1.查询主键 /*查询某个表中存在的约束*/ select * from user_constraints where table_name ...

  9. Fisher线性判别分析(二分类)

    LDA(Linear Discriminant Analysis)是一种经典的线性判别方法,又称Fisher判别 分析.该方法思想比较简单:给定训练集样例,设法将样例投影到一维的直线 上,使得同类样例 ...

  10. Sunshine on my shoulders

    https://music.163.com/#/song?id=1477706 Sunshine on my shoulders makes me happy照在我肩上的阳光让我欢乐Sunshine ...