MyBatis乐观锁与悲观锁——解决现身难点(转)

悲观锁应用

亟需选取数据库的锁机制,比如SQL SERVER
的TABLOCKX(排它表锁) 此选项被选中时,SQL  Server 
将在方方面面表上置排它锁直至该命令或业务截至。那将预防其余进度读取或修改表中的数据。

SqlServer中使用

Begin Tran
select top 1 @TrainNo=T_NO
         from Train_ticket   with (UPDLOCK)   where S_Flag=0

      update Train_ticket
         set T_Name=user,
             T_Time=getdate(),
             S_Flag=1
         where T_NO=@TrainNo
commit

大家在询问的时候使用了with
(UPDLOCK)选项,在询问记录的时候我们就对记录加上了履新锁,表示大家将要对此记录举行更新.
注意更新锁和共享锁是不争持的,相当于其他用户仍是可以查询此表的情节,不过和换代锁和排它锁是争论的.所以其他的换代用户就会阻塞.

并发控制机制

  悲观锁:假定会发出并发争辨,屏蔽一切只怕违反数据完整性的操作。[1]

  乐观锁:若是不会发生并发争执,只在提交操作时检查是或不是违背数据完整性。[1] 乐观锁无法解决脏读的题材。

 

乐观锁应用

乐观锁介绍:

  乐观锁( Optimistic Locking )
相对悲观锁而言,乐观锁假若认为数额一般景色下不会造成争执,所以在数码举办提交更新的时候,才会规范对数据的抵触与否举办检测,如果发现争持了,则让重返用户错误的音信,让用户决定如何去做。那么大家怎么着达成乐观锁呢,一般的话有以下2种办法:

  1.使用数量版本(Version)记录机制达成,那是无忧无虑锁最常用的一种达成格局。何谓数据版本?即为数据增添一个版本标识,一般是由此为数据库表增加一个数字类型的
“version”
字段来兑现。当读取数据时,将version字段的值一同读出,数据每更新一遍,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的此时此刻版本音讯与第二回取出来的version值进行比对,倘使数额库表当前版本号与第三回取出来的version值相等,则给予更新,否则认为是过期数据。用上面的一张图来表明:MyBatis 1

如上图所示,假如更新操作顺序执行,则数据的本子(version)依次递增,不会发出争论。可是如若发生有两样的工作操作对同样版本的数量进行修改,那么,先付给的操作(图中B)会把数量version更新为2,当A在B之后提交更新时发现数目的version已经被涂改了,那么A的翻新操作会战败。

 

2.乐天锁定的第三种完结情势和率先种大约,同样是在急需开展锁控制的table中增添一个字段,名称无所谓,字段类型动用时间戳(timestamp),
和方面的version类似,也是在创新提交的时候检查当前数据库中多少的时日戳和调谐更新前取到的时日戳进行相比,如果相同则OK,否则就是本子冲突。

 

使用举例:以MySQL InnoDB为例

要么拿此前的实例来举:商品goods表中有一个字段status,status为1意味商品未被下单,status为2表示商品已经被下单,那么大家对某个商品下单时务必确保该商品status为1。假若商品的id为1。

 

下单操作包含3步骤:

1.查询出商品新闻

select (status,status,version) from t_goods where id=#{id}

2.基于商品音讯生成订单

3.改动商品status为2

update t_goods 

set status=2,version=version+1where id=#{id} and version=#{version};

  那么为了选取乐观锁,大家率先修改t_goods表,增添一个version字段,数据暗中同意version值为1。

  t_goods表初步数据如下:

  对于乐观锁的落到实处,我应用MyBatis来进展实践,具体如下:

Goods实体类:

MyBatis 2

/**
 * ClassName: Goods <br/>
 * Function: 商品实体. <br/>*/public class Goods implements Serializable {    /**
     * serialVersionUID:序列化ID.     */
    private static final long serialVersionUID = 6803791908148880587L;    
    /**
     * id:主键id.     */
    private int id;    
    /**
     * status:商品状态:1未下单、2已下单.     */
    private int status;    
    /**
     * name:商品名称.     */
    private String name;    
    /**
     * version:商品数据版本号.     */
    private int version;
    
    @Override    public String toString(){        return "good id:"+id+",goods status:"+status+",goods name:"+name+",goods version:"+version;
    }    //setter and getter}

MyBatis 3

 

GoodsDao

MyBatis 4

/**
 * updateGoodsUseCAS:使用CAS(Compare and set)更新商品信息
 * @param goods 商品对象
 * @return 影响的行数 */int updateGoodsUseCAS(Goods goods);

MyBatis 5

 

mapper.xml

MyBatis 6

<update id="updateGoodsUseCAS" parameterType="Goods">
    <![CDATA[
        update t_goods
        set status=#{status},name=#{name},version=version+1
        where id=#{id} and version=#{version}    ]]></update>

MyBatis 7

 

 

GoodsDaoTest测试类

MyBatis 8

@Testpublic void goodsDaoTest(){    int goodsId = 1;    //根据相同的id查询出商品信息,赋给2个对象
    Goods goods1 = this.goodsDao.getGoodsById(goodsId);
    Goods goods2 = this.goodsDao.getGoodsById(goodsId);    
    //打印当前商品信息    System.out.println(goods1);
    System.out.println(goods2);    
    //更新商品信息1
    goods1.setStatus(2);//修改status为2
    int updateResult1 = this.goodsDao.updateGoodsUseCAS(goods1);
    System.out.println("修改商品信息1"+(updateResult1==1?"成功":"失败"));    
    //更新商品信息2
    goods1.setStatus(2);//修改status为2
    int updateResult2 = this.goodsDao.updateGoodsUseCAS(goods1);
    System.out.println("修改商品信息2"+(updateResult2==1?"成功":"失败"));
}

MyBatis 9

 

输出结果:

good id:1,goods status:1,goods name:道具,goods version:1  
good id:1,goods status:1,goods name:道具,goods version:1  
修改商品信息1成功  
修改商品信息2失败

 

 

说明:

  在GoodsDaoTest测试方法中,大家同时查获同一个版本的数据,赋给差其余goods对象,然后先修改good1对象然后执行更新操作,执行成功。然后我们修改goods2,执行更新操作时提示操作失败。此时t_goods表中数量如下:

MyBatis 10

mysql> select * from t_goods;+----+--------+------+---------+| id | status | name | version |+----+--------+------+---------+|  1 |      2 | 道具 |       2 ||  2 |      2 | 装备 |       2 |+----+--------+------+---------+2 rows in setmysql>

MyBatis 11

 

  我们得以看到 id为1的数额version已经在第四回立异时修改为2了。所以大家创新good2时update where条件已经不匹配了,所以更新不会水到渠成,具体sql如下:

update t_goods 
set status=2,version=version+1where id=#{id} and version=#{version};

  那样大家就兑现了乐观锁

 

  在多用户环境中,在同一时间只怕会有三个用户更新相同的笔录,这会发生争执。那就是令人侧目标并发性难点。

引言

原稿地址:http://www.javaweb1024.com/java/JavaWebzhongji/2015/09/06/847.html
   尊重原创,请访问原文地址

杰出的争论有:

结论

  在事实上生产条件之中,即使并发量不大且不允许脏读,可以采纳悲观锁化解出现难题;但只要系统的面世相当大的话,悲观锁定会带来尤其大的性质难点,所以我们就要选拔乐观锁定的方法.

干什么必要锁(并发控制)?

  • 丢失更新:一个作业的换代覆盖了其余业务的更新结果,就是所谓的更新丢失。例如:用户A把值从6改为2,用户B把值从2改为6,则用户A丢失了她的换代。

  • 脏读:当一个业务读取此外落成一半事务的笔录时,就会时有发生脏读取。例如:用户A,B看到的值都以6,用户B把值改为2,用户A读到的值仍为6。

为了缓解那一个出现带来的题材。 大家须要引入并发控制机制。

网站地图xml地图