悲观锁、乐观锁、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. ros源的移除

    在Ubuntu上卸载了ros系统后,每次运行源更新命令 sudo apt update 都会报错,提示ros源找不到等问题. 这时,只需要 cd /etc/apt/souce.list.d sudo ...

  2. GGTalk 开源即时通讯系统源码剖析之:客户端全局缓存及本地存储

    继上篇<GGTalk 开源即时通讯系统源码剖析之:虚拟数据库>详细介绍了 GGTalk 内置的虚拟的数据库,无需部署真实数据库便能体验GGTalk的全部功能,虚拟数据库将极大地简化服务端的 ...

  3. 【opencv】传统图像识别:hog+svm行人识别实战

    实战工具:python3.7+pycharm+opencv4.6算法知识:HOG特征提取.SVM模型构建实战目的:本次实战的目的是熟悉HOG+SVM工作流算法,初步掌握图像分类的传统算法.实战记录:本 ...

  4. quarkus数据库篇之四:本地缓存

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本文是<quarkus数据库篇> ...

  5. .NET周刊【8月第2期 2023-08-14】

    本周由于Myuki大佬感染新冠,国际板块暂停更新一周,将在下周补齐,所以本周只有国内板块. 国内文章 解决 Blazor 中因标签换行导致的行内元素空隙问题 https://www.cnblogs.c ...

  6. 【JMeter】常用线程组设置策略

    常用线程组设置策略 目录 常用线程组设置策略 一.前言 二.单场景基准测试 1.介绍 2.线程组设计 3.测试结果 三.单场景并发测试 1.介绍 2.线程组设计 3.测试结果 四.单场景容量/爬坡测试 ...

  7. 如何用PHP写接口

    当用PHP编写API接口时,可以使用PHP中的框架(如Laravel.Symfony.CodeIgniter等)来简化开发过程.接下来,以使用Laravel框架为例,提供一个简单的示例代码: 首先,确 ...

  8. 在Vue2和Vue3中JSX的使用集锦

    Vue2安装JSX支持 有时候,我们使用渲染函数(render function)来抽象组件,而渲染函数使用Vue的h函数来编写Dom元素相对template语法差别较大,体验不佳,这个时候就派 JS ...

  9. MPI转以太网Plus模块Modbus连接两台变频器通信案例

    MPI转以太网Plus模块Modbus主站连接两台变频器通信案例 MPI转以太网Plus模块连接200PLC无需编程实现Modbus主从站功能与2台变频器modbus通信:以下就是MPI转以太网模块作 ...

  10. C语言指针函数和函数指针区别(转)

    C语言函数指针和指针函数的区别C和C++中经常会用到指针,和数据项一样,函数也是有地址的,函数的地址是存储其机器语言代码的内存的开始地址. 指针函数和函数指针经常会混淆,一个是返回指针的函数,另一个是 ...