悲观锁、乐观锁、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. Spark RDD惰性计算的自主优化

    原创/朱季谦 RDD(弹性分布式数据集)中的数据就如final定义一般,只可读而无法修改,若要对RDD进行转换或操作,那就需要创建一个新的RDD来保存结果.故而就需要用到转换和行动的算子. Spark ...

  2. 《数据结构-C语言》单链表

    @ 目录 单链表 结构定义 初始化 建立 清空 求表长 判断是否为空表 取值 查找 插入 删除 销毁 遍历打印 测试 单链表 结构定义 #include <stdio.h> #includ ...

  3. Frida环境配置

    目录 安装Linux客户端 配置Android服务端 测试运行效果 官方手册 安装Linux客户端 github地址:https://github.com/frida/frida pip instal ...

  4. 【目标检测】RCNN算法实现

    一.前言 RCNN(Regions with CNN features)算法由Ross Girshick在2014年的论文"Rich feature hierarchies for accu ...

  5. 在 Net7.0环境下测试了 Assembly.Load、Assmebly.LoadFile和Assembly.LoadFrom的区别

    一.简介 很长时间没有关注一些C#技术细节了,主要在研究微服务.容器.云原生.编批等高大上的主题了,最近在写一些框架的时候,遇到了一些和在 Net Framework 框架下不一样的情况,当然了,我今 ...

  6. 鞭尸没 jj

      提前退役了.现在我想说一点无关紧要的闲话.   与其说是 OI 回忆录,不如说是对这主线明确的六年做的一个梳理,倒不一定 OI 强相关. 壹.零度下的相遇 视线就这样交叠 与你   最初接触到 O ...

  7. Solution -「洛谷 P4688」「YunoOI 2016」掉进兔子洞

    Description (Link)[https://www.luogu.com.cn/problem/P4688]. 每次询问三个区间,把三个区间中同时出现的数一个一个删掉,问最后三个区间剩下的数的 ...

  8. 各种SQL连接符Join

    一.连接符分类,内连接,外连接 1.内连接:Inner Join简写Join. 2.外连接:Left Outer Join 简写Left Join:Right Outer Join 简写Right J ...

  9. 前端三件套系例之JS——JavaScript基础、JavaScript基本数据类型、JavaScript函数

    文章目录 1 JavaScript基础 1.JavaScript是什么 2.JavaScript介绍 2-1 ECMAScript和JavaScript的关系 2-2 ECMAScript的历史 3. ...

  10. WebKit Insie: Active 样式表

    WebKit Inside: CSS 样式表的匹配时机介绍了当 HTML 页面有不同 CSS 样式表引入时,CSS 样式表开始匹配的时机.后续文章继续介绍 CSS 样式表的匹配过程,但是在匹配之前,首 ...