数据库的悲观锁和乐观锁

悲观锁的介绍

悲观锁就是对数据的冲突持悲观态度,也就是假设数据肯定会发生冲突,所以在数据开始读取的时候就把数据锁定住。

使用场景举例 以Mysql的InnoDB为例

书籍表book,id为商品id(主键),isOnline是否上线, 1代表上线,0代表下线,那么我们如果要对书籍进行下线,就需要将online置为0,假设id为1

  1. 如果不采用锁
    1> select * from book where id = 1; //查询对应的书籍
    2> insert into pay_log …;//生成订单
    3> update book set isOnline = 0 where id = 1; //做下线处理

    这种方式在单线程的环境下不会有问题,如果是在高并发的场景下,这样的处理就会出现问题了。当一个人下单后,准备执行第三步的时候(还没执行时书籍还是在线),另一个人也下单,并且执行完了1、2、3步,此时的书籍已经调整为下线状态,前一个人的依然执行第三步,就会重置两次下线状态
    该书籍也被下单了两次。这样就造成数据错误,所以说这种方式是不安全的。

  2. 采用悲观锁
    悲观锁的原理就是当一个人将该书籍查询出来的时候,就将当前的数据锁定住,等到前一个人修改完以后,才解锁,那么在这个过程中,数据被锁住了,
    其他人也就不能对其进行修改了。

    当我们要使用悲观锁的时候,必须关闭数据库的自动提交属性,因为Mysql默认是autocommit自动提交属性。
    set autocommit = 0;

    然后就可以执行我们的业务了:
           	begin;
     	select * from book where id = 1 for update;
     	insert into pay_log ......;//生成订单
     	update book set isOnline = 0 where id = 1; //做下线处理
     	commit;
    

    与普通查询不一样的是使用的是 select…for update,这样就实现了悲观锁。这样id=1的那条数据就被锁定住了,等当前事务操作结束后才会解锁。
    这样就可以保证数据不会被其他事务影响。

  3. 补充
    Mysql的InnoDB默认采用Row-level Lock,所以只有明确指定(只能是id=1,不能是id>1这样的形式)主键才会执行Row Lock(只会锁住对应的数据),则将执行Table Lock(将整个表单锁住)。

乐观锁的介绍

悲观锁也存在着自身的不足,并不适用于任何场景。因为悲观锁大多数情况下依靠的是数据库的锁机制实现的,以保证操作最大程度的独占性。如果这个事务很长,那么加锁的时间也会很长,其他用户长时间得不到响应,影响到了系统的并发访问,同时对数据库的性能开销也是很大的。所以和悲观锁对应的,有了乐观
锁。
乐观锁认为数据一般情况不会冲突,所以在数据进行提交更新的时候,才会对数据的冲入与否进行检验,如果发现冲突了,则返回用户错误的信息,让用户决定如何做。
悲观锁的实现大多数基于版本或者时间戳实现:当读取数据的时候,将version字段一并取出,数据每更新一次,就将version + 1。当我们提交更新的时候,断当前版本信息是否与第一次取出来的版本值相等,如果相等,则更新,否则认为是过期数据,拒绝更新,让用户重新操作。

使用场景举例 以Mysql的InnoDB为例

还是使用上边的书籍表book,那么为了使用乐观锁,增加一个version字段,默认是1.
mapper.xml中的sql:
update book set isOnline = 0, version = #{version} + 1 where id = 1 and version = #{version}

当同时读取id=1的两条数据时,两条数据中的version都是1,当将其中的一条数据进行更新后,version变为2,等另外一条数据进行更新的时候,因为version1,而数据库中的version为2,更新的时候就会找不到对应的那条数据,更新失败。类似于SVN的原理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值