当前位置: 首页 > news >正文

spring data jpa mysql 悲观锁

实践悲观锁。


业务模型是User访问target,target的点击数量+1


一般流程是 读——count+1——写


如果在并发下,存在count计数失误的情况,可以以如下方法验证:

为了模拟放大并发的现象,在读与写之间➕ sleep

读-sleep(6000) —— count++ ——写

    public PageResult detail(Integer userId, Integer id) {
        EquityMoney equityMoney = equityMoneyDao.findById(id);
        
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        equityMoney.setReading_quantity(equityMoney.getReading_quantity()+1);
        equityMoneyDao.save(equityMoney);
        return PageResult.genSuccess(equityMoney);
    }

    @Query("SELECT cd from EquityMoney cd  where cd.id=:id ")
    EquityMoney findById(@Param("id")Integer id);



测试配置1:

准备一台服务器A
准备浏览器两个标签,同时访问A

访问时两边同时sleep

结果两边结果都显示访问量(reading_quantity)为相同值


现在我们修改一下,为findById加上悲观锁

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("SELECT cd from EquityMoney cd  where cd.id=:id ")
    EquityMoney findById(@Param("id")Integer id);

再行相同测试,此时看到访问量两个标签为递增状态了,说明悲观锁起作用了,线程1读取时加锁,导致线程2无法读取,直到线程1的reading_quantity+1并写入操作,事务完成,线程2才读取,此时再+1,所以两边访问量差1

注:

1 若换为PESSIMISTIC_READ,第一个请求成功,第二个请求报了

Deadlock found when trying to get lock; try restarting transaction


为什么?

查找了下资料:

(1)http://suene.iteye.com/blog/1756295



Java代码   收藏代码
  1. @Lock(LockModeType.PESSIMISTIC_READ)  
  2. public List<User> findByUsername(String username);  

对应的 sql 就是: 
Java代码   收藏代码
  1. select * from t_user where username=? lock in share mode  



Java代码   收藏代码
  1. @Lock(LockModeType.PESSIMISTIC_WRITE)  
  2. public List<User> findByUsername(String username);  

对应的 sql 就是: 
Java代码   收藏代码
  1. select * from t_user where username=? for update  

所以此二者的区别是 共享锁和排他锁


(2)http://blog.csdn.net/cug_jiang126com/article/details/50544728


SELECT ... LOCK IN SHARE MODE走的是IS锁(意向共享锁),即在符合条件的rows上都加了共享锁,这样的话,其他session可以读取这些记录,也可以继续添加IS锁,但是无法修改这些记录直到你这个加锁的session执行完成(否则直接锁等待超时)。

SELECT ... FOR UPDATE 走的是IX锁(意向排它锁),即在符合条件的rows上都加了排它锁,其他session也就无法在这些记录上添加任何的S锁或X锁。如果不存在一致性非锁定读的话,那么其他session是无法读取和修改这些记录的,但是innodb有非锁定读(快照读并不需要加锁),for update之后并不会阻塞其他session的快照读取操作,除了select ...lock in share mode和select ... for update这种显示加锁的查询操作
(*快照读&当前读的概念:innodb当前读&快照读)
通过对比,发现for update的加锁方式无非是比lock in share mode的方式多阻塞了select...lock in share mode的查询方式,并不会阻塞快照读。

此外,分析一下这个为啥会死锁

在 PESSIMISTIC_READ & PESSIMISTIC_WRITE 与 共享锁 & 排它锁 中有详细解答



2 此悲观锁基于事务(因为mysql的锁基于单次会话,会话结束后就会解锁,试想一下,select for update 不加事务,等于执行完这句话就解锁了,等于没加),且一般在service层上加

@Transactional

测试结果

 有事务注解无事务注解
innodbokno
myisamno

验证了实现行级悲观锁必须满足以下两个条件:

(1)innordb

(2)事务(确保同一mysql会话)

此外,Myisam引擎也可实现,但是需要显式调用table lock


数据库锁比起代码锁的好处:

(1)数据库锁适用于集群

(2)数据库锁可以精确到行,锁代码会大面积误伤不需要锁的其它操作

测试配置2:

准备两台服务器

A  sleep

B none sleep


前提:

@Transcation+innodb

结果:

先访问A,再访问B

相同的id

发现B随着A的sleep一起阻塞了,证明id这条数据被锁导致B服务器线程阻塞


A id1,B id2

发现并未发生阻塞的情况,证明inndb mysql是行级锁,id1与id2并不互相影响

rr下,唯一索引当前读上reord lock,非唯一上 next-key lock

具体看:

 相同id不同id
innodbB阻塞B不阻塞
myisamB不阻塞


注:全过程未涉及到某些博客提的 mysql autocommit 

应该是由spring 的@Transaction注解 和 hibernate代劳了




参考:


锁、事务和同步看这篇就够了(1)

1.以一个经典财务案例并配合图讲述了为何要用锁,用代码锁?还是数据库锁?

2.使用乐观锁的代码

3.使用悲观锁的代码


Spring Data JPA,基础学习笔记.

1.高度概述了spring data jpa 的使用

2.点出了悲观锁中 PESSIMISTIC_READ 与 PESSIMISTIC_WRITE在编译后的sql区别,在于 lock in share mode 和 for update


深入理解SELECT ... LOCK IN SHARE MODE和SELECT ... FOR UPDATE

1.讲述了 共享锁和排它锁的区别,最主要在于共享锁阻塞写,排它锁阻塞读写

2.以一个经典订单案例讲述了为何要用锁,描述了一个经典死锁案例

3.总结了一般应用场景-共享:跨表  排它:同表一致性要求


数据库悲观锁和乐观锁

1.详细描述了oracle和mysql的锁

2.以经典订单案例,描述了加锁前与加悲观锁后的效果

3.提到了autocommit(mysql),虽然我没用到

4.以大量实例证明 mysql 行级锁、表级锁随着明确指定主键或索引的关系


MySQL数据库锁机制之MyISAM引擎表锁和InnoDB行锁详解

主要室myisam和innodb区别,没怎么看


spring jpa 行级锁的实现

1.简单清晰的一个spring data jpa 悲观行级锁(排它)的代码实例

2.强调了service层开启事务

3.强调了必须明确主键(我认为或添置索引),才会启用行级锁,否则为表级锁


使用 @Lock 注解实现Spring JAP锁

1.自上而下简述了spring data jpa 锁种类

2.简单清晰的一个spring data jpa 悲观行级锁(共享,我个人认为这里应该用排它,共享锁阻塞save操作,这个例子感觉室死锁)的代码实例

3.强调了service层开启事务





8.16日语:

能否

update xxx cd set cd.reading_qualtity = cd.reading_qualtity+1  where cd.id=:id 


转载于:https://www.cnblogs.com/silyvin/p/9106788.html

相关文章:

  • 迅为IMX6开发板支持全网通4G模块丨GPS模块丨WIFI蓝牙丨千兆以太网
  • hackerrank Diameter Minimization
  • 如何开发一个Servlet
  • HDU1023 Train Problem II
  • Linux 定时任务的学习
  • 什么是编程语言
  • JavaSE--【转】网络安全之证书、密钥、密钥库等名词解释
  • Python基础 :正则表达式
  • 是否有网络
  • 是时候学一波STL了。。。
  • html特殊字符的html,js,css写法汇总
  • 19_传智播客iOS视频教程_类和对象
  • Msql入门实战之下
  • Repeater的使用及其鼠标特效,行链接的使用
  • Linux下汇编语言学习笔记15 ---
  • [译] 怎样写一个基础的编译器
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • CAP理论的例子讲解
  • CSS魔法堂:Absolute Positioning就这个样
  • C语言笔记(第一章:C语言编程)
  • fetch 从初识到应用
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • java 多线程基础, 我觉得还是有必要看看的
  • js学习笔记
  • Magento 1.x 中文订单打印乱码
  • Mithril.js 入门介绍
  • ng6--错误信息小结(持续更新)
  • PHP 7 修改了什么呢 -- 2
  • PHP 的 SAPI 是个什么东西
  • Python语法速览与机器学习开发环境搭建
  • VirtualBox 安装过程中出现 Running VMs found 错误的解决过程
  • vue从创建到完整的饿了么(18)购物车详细信息的展示与删除
  • 百度地图API标注+时间轴组件
  • 彻底搞懂浏览器Event-loop
  • 从零开始在ubuntu上搭建node开发环境
  • 技术攻略】php设计模式(一):简介及创建型模式
  • 一天一个设计模式之JS实现——适配器模式
  • ​低代码平台的核心价值与优势
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • #我与Java虚拟机的故事#连载07:我放弃了对JVM的进一步学习
  • $().each和$.each的区别
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (2.2w字)前端单元测试之Jest详解篇
  • (22)C#传智:复习,多态虚方法抽象类接口,静态类,String与StringBuilder,集合泛型List与Dictionary,文件类,结构与类的区别
  • (C++17) std算法之执行策略 execution
  • (Oracle)SQL优化技巧(一):分页查询
  • (附源码)spring boot建达集团公司平台 毕业设计 141538
  • (一)Java算法:二分查找
  • (终章)[图像识别]13.OpenCV案例 自定义训练集分类器物体检测
  • .gitattributes 文件
  • .NET Core6.0 MVC+layui+SqlSugar 简单增删改查
  • .NET Framework 3.5中序列化成JSON数据及JSON数据的反序列化,以及jQuery的调用JSON
  • .NET 中小心嵌套等待的 Task,它可能会耗尽你线程池的现有资源,出现类似死锁的情况
  • .NET和.COM和.CN域名区别
  • .NET命名规范和开发约定