MYSQL 乐观锁与悲观锁

MYSQL 乐观锁与悲观锁1 乐观锁 乐观锁 OptimisticLo 顾名思义 就是很乐观 每次去拿数据的时候都认为别人不会修改 所以不会上锁 但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据 乐观锁适用于读多写少的应用场景 这样可以提高吞吐量 乐观锁 假设不会发生并发冲突 只在提交操作时检查是否违反数据完整性 乐观锁一般来说有以下 2 种方式 使用数据版本 Version 记录机制实现 这是

标题

1.乐观锁:

乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。乐观锁适用于读多写少的应用场景,这样可以提高吞吐量。

乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。

乐观锁一般来说有以下2种方式:

  1. 使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。何谓数据版本?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。
  2. 使用时间戳(timestamp)。乐观锁定的第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。

Java JUC中的atomic包就是乐观锁的一种实现,AtomicInteger 通过CAS(Compare And Set)操作实现线程安全的自增。

2.悲观锁:

悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。

悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。

Java synchronized 就属于悲观锁的一种实现,每次线程要修改数据时都先获得锁,保证同一时刻只有一个线程能操作数据,其他线程则会被block。

3.举例:

乐观锁:

当对用户进行积分处理的时候就用到了乐观锁中的使用版本号控制

1.用户表的设计。

CREATE TABLE `yb_user` ( `user_id` varchar(64) NOT NULL, `user_name` varchar(40) DEFAULT NULL, `head_img_url` varchar(128) DEFAULT NULL COMMENT '头像', `point` int(10) DEFAULT NULL COMMENT '总积分', `version` bigint(20) DEFAULT '0' COMMENT '版本号', `play_num` int(11) DEFAULT NULL COMMENT '每天可玩次数', `created_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间', `updated_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`user_id`), UNIQUE KEY `id_user_id` (`user_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表'; 

2.对用户进行增加与减少积分的时候加上版本号

Mapper:

 / * 新增积分 * * @param userId 用户编号 * @param point 增加积分 * @param version 版本号 * @return */ int addPoint(@Param("userId") String userId, @Param("point") Integer point, @Param("version") int version); / * 减少积分 * * @param userId 用户编号 * @param point 增加积分 * @param version 版本号 * @return */ int lessPoint(@Param("userId") String userId, @Param("point") Integer point, @Param("version") int version); 

xml:

<update id="addPoint"> update yb_user set point = point+#{point}, version = version+1 where user_id = #{userId} AND version=#{version}  
     update> <update id="lessPoint"> update yb_user set point = point-#{point}, version = version+1 where user_id = #{userId} AND version = #{version} AND point > #{point}  
      update> 

3.service层调用,我们可以看到当用户增加积分成功才进行接下来的操作,所以可以防止相同用户的并发操作

if (ybUserMapper.addPoint(userId, ybTask.getPoint(), user.getVersion()) > 0) { pointDetail.setPoint(ybTask.getPoint()); if (taskType.equals("sing")) { //答题记录的类型 pointDetail.setType(PointTypeEm.ANSWER.getCode()); } else { //其他任务类型 pointDetail.setType(PointTypeEm.TASK_POINT.getCode()); } ybPointDetailMapper.insertSelective(pointDetail); } 
悲观锁:

需要使用数据库的锁机制,比如SQL SERVER 的TABLOCKX(排它表锁) 此选项被选中时,SQL Server 将在整个表上置排它锁直至该命令或事务结束。这将防止其他进程读取或修改表中的数据。

SqlServer中使用

我们在查询的时候使用了with (UPDLOCK)选项,在查询记录的时候我们就对记录加上了更新锁,表示我们即将对此记录进行更新. 注意更新锁和共享锁是不冲突的,也就是其他用户还可以查询此表的内容,但是和更新锁和排它锁是冲突的.所以其他的更新用户就会阻塞.

4.总结:

​ 在实际生产环境里边,如果并发量不大且不允许脏读,可以使用悲观锁解决并发问题;但如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择乐观锁定的方法.

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/179355.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月26日 下午3:46
下一篇 2026年3月26日 下午3:47


相关推荐

  • php ecshop二次开发,ecshop二次开发对ecshop系统框架分析

    php ecshop二次开发,ecshop二次开发对ecshop系统框架分析ecshop 二次开发对 ecshop 系统框架分析第一 整体框架结构 整个系统是层结构有数据层有外观层比如我们 index php 是外观层 和用户打交道的层 那推荐产品来说吧在 index php 是对数据层函数的调用 get recommend goods new 这个函数那么他是在 include 目录下的 lib goods php 进行定义既数据层 第二 重要目录 首先

    2026年3月19日
    2
  • spring容器初始化过程中出现异常_spring容器什么时候启动

    spring容器初始化过程中出现异常_spring容器什么时候启动前言我们知道,spring的启动其实就是容器的启动,而一般情况下,容器指的其实就是上下文ApplicationContext。AbstractApplicationContext作为整个A

    2022年8月17日
    10
  • 白丝少女被挠脚心

    白丝少女被挠脚心

    2026年3月15日
    3
  • php stripslashes()函数,PHP中 stripslashes() 函数 和 addslashes() 函数

    php stripslashes()函数,PHP中 stripslashes() 函数 和 addslashes() 函数本篇将为大家介绍 PHP 中 stripslashes 函数和 addslashes 函数 一 stripslashes 删除反斜杠定义和用法 stripslashes 函数删除由 addslashes 函数添加的反斜杠 提示 该函数可用于清理从数据库中或者从 HTML 表单中取回的数据

    2026年3月26日
    1
  • 更改nginx默认端口_更改端口号

    更改nginx默认端口_更改端口号一、查看当前Nginx配置信息打开nginx根目录下conf文件夹,点击编辑nginx.conf文件。Tips:Windows系统用记事本打开会使utf-8文件头部加上BOM头,这会导致配置文件不能正常读取,Nginx启动失败。因此推荐使用SublimeText3之类的编辑器打开。 server{listen80;#默认端口号…

    2025年10月2日
    45
  • synchronized偏向锁和轻量级锁_偏向锁/轻量级锁/重量级锁的原理

    synchronized偏向锁和轻量级锁_偏向锁/轻量级锁/重量级锁的原理今天简单了解了一下java轻量级锁和重量级锁以及偏向锁。看了看这篇文章觉得写的不错原文链接java 偏向锁、轻量级锁及重量级锁synchronized原理Java对象头与Monitorjava对象头是实现synchronized的锁对象的基础,synchronized使用的锁对象是存储在Java对象头里的。对象头包含两部分:Mark Word 和 Class Metadata Address其中Mark Word在默认情况下存储着对象的HashCode、分代年龄、锁标记位等以下是32位JVM的

    2022年8月9日
    11

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号