zoukankan      html  css  js  c++  java
  • 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 


  • 相关阅读:
    Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
    发送广播重新挂载SD卡,使图库可以及时显示自己保存的图片(无需手机重启)
    Android学习记录(10)—Android之图片颜色处理
    Android学习记录(9)—Android之Matrix的用法
    撕美女衣服应用的原理及做法
    以最省内存的方式把大图片加载到内存及获取Exif信息和获取屏幕高度和宽度的新方法
    通过广播关闭应用程序(每个Activity)和连续点击两次返回键关闭应用程序
    Android有效解决加载大图片时内存溢出的问题
    有关ViewFlipper的使用及设置动画效果的讲解
    Android学习记录(8)—Activity的四种加载模式及有关Activity横竖屏切换的问题
  • 原文地址:https://www.cnblogs.com/silyvin/p/9106788.html
Copyright © 2011-2022 走看看