mysql悲观锁有哪些?
mysql悲观锁是相对悲不雅锁而言,悲观锁假设认为数据一样状况下不会造成冲突,所以在数据停止提交更新的时候,才会正式对数据的冲突与否停止检测,假如发明冲突了,则让返回会员错误的信息,让会员决议怎样去做。
mysql悲观锁是( Optimistic Locking )相对悲不雅锁而言,悲观锁假设认为数据一样状况下不会造成冲突,所以在数据停止提交更新的时候,才会正式对数据的冲突与否停止检测,假如发明冲突了,则让返回会员错误的信息,让会员决议怎样去做。
那么我们怎样实现悲观锁呢,一样来说有以下2种方式:
1、使用数据版本(Version)记载机制实现,这是悲观锁最常用的一种实现 方式。何谓数据版本?即为数据增添一个版本标识,一样是通过为数据库表增添一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。
当我们提交更新的时候,推断数据库表对应记载 的当前版本信息与第一次取出来的version值停止比对,假如数据库表当前版本号与第一次取出来的version值相等,则予以更新,不然认为是过期数 据。用下面的一张图来说明:
如上图所示,假如更新操纵次序施行,则数据的版本(version)顺次递增,不会发生冲突。但是假如发生有不一样的业务操纵对统一版本的数据停止修 改,那么,先提交的操纵(图中B)会把数据version更新为2,当A在B之后提交更新时发明数据的version已经被修改了,那么A的更新操纵会失败。
2、悲观锁定的第二种实现方式和第一种差不多,一样是在需要悲观锁操纵的table中增添一个字段,名称无所谓,字段类型使用时间戳 (timestamp), 和上面的version相似,也是在更新提交的时候检查当前数据库中数据的时间戳和本人更新前取到的时间戳停止对照,假如一致则OK,不然就是版本冲突。
相关学习引荐:mysql视频教程
使用举例:
以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+1 where id=#{id} and version=#{version};
那么为了使用悲观锁,我们第一修改t_goods表,增添一个version字段,数据默许version值为1。
t_goods
表初始数据如下:
mysql> select * from t_goods; +----+--------+------+---------+ | id | status | name | version | +----+--------+------+---------+ | 1 | 1 | 道具 | 1 | | 2 | 2 | 装备 | 2 | +----+--------+------+---------+ 2 rows in set mysql>
关于悲观锁的实现,我使用MyBatis来停止实践,详细如下:
Goods实体类:
/** * ClassName: Goods <br/> * Function: 商品实体. <br/> * date: 2013-5-8 上午09:16:19 <br/> * @author chenzhou1025@126.com */ 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 }
GoodsDao
/** * updateGoodsUseCAS:使用CAS(Compare and set)更新商品信息. <br/> * * @author chenzhou1025@126.com * @param goods 商品对象 * @return 影响的行数 */ int updateGoodsUseCAS(Goods goods);
mapper.xml
<update id="updateGoodsUseCAS" parameterType="Goods"> <![CDATA[ update t_goods set status=#{status},name=#{name},version=version+1 where id=#{id} and version=#{version} ]]> </update>
GoodsDaoTest测试类
@Test public 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?"成功":"失败")); }
输出结果:
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
表中数据如下:
mysql> select * from t_goods; +----+--------+------+---------+ | id | status | name | version | +----+--------+------+---------+ | 1 | 2 | 道具 | 2 | | 2 | 2 | 装备 | 2 | +----+--------+------+---------+ 2 rows in set mysql>
我们可以看到 id为1的数据version已经在第一次更新时修改为2了。所以我们更新good2时update where前提已经不匹配了,所以更新不会成功,详细sql如下:
update t_goods set status=2,version=version+1 where id=#{id} and version=#{version};
这样我们就实现了悲观锁。
以上就是mysql悲观锁是啥?的具体内容,更多请关注百分百源码网其它相关文章!