美文网首页
悲观锁与乐观锁

悲观锁与乐观锁

作者: 杰哥长得帅 | 来源:发表于2018-12-14 10:17 被阅读15次

    悲观锁

    总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如 Java 里面的同步原语 synchronized 关键字的实现也是悲观锁

    通常所说的 “一锁二查三更新” 即指的是使用悲观锁。通常来讲在数据库上的悲观锁需要数据库本身提供支持,即通过常用的 select … for update 操作来实现悲观锁。当数据库执行 select for update 时会获取被 select 中的数据行的行锁,因此其他并发执行的 select for update 如果试图选中同一行则会发生排斥(需要等待行锁被释放),因此达到锁的效果。select for update 获取的行锁会在当前事务结束时自动释放,因此必须在事务中使用

    乐观锁

    顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于 write_condition 机制,其实都是提供的乐观锁。在 Java 中 java.util.concurrent.atomic 包下面的原子变量类就是使用了乐观锁的一种实现方式 CAS 实现的

    乐观锁,大多是基于数据版本(Version)记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现

    适用场景

    悲观锁:比较适合写入操作比较频繁的场景,如果出现大量的读取操作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低了系统的吞吐量

    乐观锁:比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询操作,降低了系统的吞吐量。如果并发量很高时使用乐观锁的话,会导致很多的并发事务回滚、操作失败

    乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断 retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适,之所以用悲观锁就是因为两个用户更新同一条数据的概率高,也就是冲突比较严重的情况下,所以才用悲观锁

    乐观锁在不发生取锁失败的情况下开销比悲观锁小,但是一旦发生失败回滚开销则比较大,因此适合用在取锁失败概率比较小的场景,可以提升系统并发性能

    悲观锁比较适合强一致性的场景,但效率比较低,特别是读的并发低。乐观锁则适用于读多写少,并发冲突少的场景

    如何选择

    • 响应速度、并发量:选择乐观锁。要么冲突失败要么快速成功。悲观锁则需要等待释放锁才能被执行
    • 冲突频率:频率高的话不应选择乐观锁,需要重试好几次,代价大。而悲观锁保证成功率
    • 重试代价:若重试代价大则选择悲观锁

    相关文章

      网友评论

          本文标题:悲观锁与乐观锁

          本文链接:https://www.haomeiwen.com/subject/narhgftx.html