1.单机应用乐观锁悲观锁,select 时怎么加排它锁?

1.1悲观锁(Pessimistic Lock):

悲观锁特点:先获取锁,再进行业务操作。

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

补充:

不同的数据库对select for update的实现和支持都是有所区别的,

oracle支持select for update no wait,表示如果拿不到锁立刻报错,而不是等待,MySQL就没有no wait这个选项。

MySQL还有个问题是select for update语句执行中所有扫描过的行都会被锁上,这一点很容易造成问题。因此如果在MySQL中用悲观锁务必要确定走了索引,而不是全表扫描。

1.2乐观锁(Optimistic Lock):

1.2.1乐观锁,也叫乐观并发控制,它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,那么当前正在提交的事务会进行回滚。

1.2.2乐观锁的特点先进行业务操作,不到万不得已不去拿锁。即“乐观”的认为拿锁多半是会成功的,因此在进行完业务操作需要实际更新数据的最后一步再去拿一下锁就好。

乐观锁在数据库上的实现完全是逻辑的,不需要数据库提供特殊的支持。

1.2.3 一般的做法是在需要锁的数据上增加一个版本号,或者时间戳,

1.3 实现方式举例如下:

乐观锁(给表加一个版本号字段) 这个并不是乐观锁的定义,给表加版本号,是数据库实现乐观锁的一种方式。

SELECT data AS old_data, version AS old_version FROM …;

根据获取的数据进行业务操作,得到new_data和new_version

UPDATE SET data = new_data, version = new_version WHERE version = old_version

if (updated row > 0) {

// 乐观锁获取成功,操作完成

} else {

// 乐观锁获取失败,回滚并重试

}

注意:

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

乐观锁还适用于一些比较特殊的场景,例如在业务操作过程中无法和数据库保持连接等悲观锁无法适用的地方。

1.4 总结:

悲观锁和乐观锁是数据库用来保证数据并发安全防止更新丢失的两种方法,例子在select ... for update前加个事务就可以防止更新丢失。悲观锁和乐观锁大部分场景下差异不大,一些独特场景下有一些差别,一般我们可以从如下几个方面来判断。

响应速度: 如果需要非常高的响应速度,建议采用乐观锁方案,成功就执行,不成功就失败,不需要等待其他并发去释放锁。'

冲突频率: 如果冲突频率非常高,建议采用悲观锁,保证成功率,如果冲突频率大,乐观锁会需要多次重试才能成功,代价比较大。

重试代价: 如果重试代价大,建议采用悲观锁。

2. 分布式锁

2.1基于数据库实现分布式锁:也分为乐观锁和悲观锁(悲观锁也叫排他锁).

2.1.1分布式乐观锁实现方式:

在数据库表中引入一个版本号(version)字段来实现的。

当我们要从数据库中读取数据的时候,同时把这个version字段也读出来,如果要对读出来的数据进行更新后写回数据库,则需要将version加1,同时将新的数据与新的version更新到数据表中,且必须在更新的时候同时检查目前数据库里version值是不是之前的那个version,如果是,则正常更新。如果不是,则更新失败,说明在这个过程中有其它的进程去更新过数据了。

场景:假设同一个账户,用户A和用户B都要去进行取款操作,账户的原始余额是2000,用户A要去取1500,用户B要去取1000,如果没有锁机制的话,在并发的情况下,可能会出现余额同时被扣1500和1000,导致最终余额的不正确甚至是负数。但如果这里用到乐观锁机制,当两个用户去数据库中读取余额的时候,除了读取到2000余额以外,还读取了当前的版本号version=1,等用户A或用户B去修改数据库余额的时候,无论谁先操作,都会将版本号加1,即version=2,那么另外一个用户去更新的时候就发现版本号不对,已经变成2了,不是当初读出来时候的1,那么本次更新失败,就得重新去读取最新的数据库余额。

使用乐观锁满足的条件:锁服务要有递增的版本号version,每次更新数据的时候都必须先判断版本号对不对,然后再写入新的版本号

2.1.2分布式悲观锁实现方式:

基于 for update 来实现加锁

当加上排它锁之后,其它线程是无法操作这条记录的。那么,这样的话,我们就可以认为获得了排它锁的这个线程是拥有了分布式锁,然后就可以执行我们想要做的业务逻辑,当逻辑完成之后,再调用上述释放锁的语句即可。

基于redis缓存实现分布式锁

a.主要是依赖redis自身的原子操作;

b. set user_key user_value NX PX100:redis从2.6.12版本开始,set命令才支持这些参数

nx : 只在在键不存在时,才对键进行设置操作,SET key value NX 效果等同于 SETNX key value;

px毫秒: 设置键的过期时间为millisecond毫秒,当超过这个时间后,设置的键会自动失效

这段命令理解:当redis中不存在user_key的时候,才会去设置一个user_key,并且这个键的值为user_value,且这个键的寻获时间为100ms

这个命令是只有在某个key不存在的时候,才会执行成功。那么当多个进程同时并发的去设置同一个key的时候,就永远只会有一个进程成功。当某个进程设置成功之后,就可以去执行业务逻辑了,等业务逻辑执行完毕之后,再去进行解锁。解锁很简单,只需要删除这个key就可以了,不过删除之前需要判断,这个key对应的value是当初自己设置的那个。

c. redis集群模式的分布式锁,可以采用redis的Redlock机制。

基于Zookeeper实现分布式锁

基于ZooKeeper,就是使用它的临时有序节点来实现的分布式锁。

原理:当某客户端要进行逻辑的加锁时,就在zookeeper上的某个指定节点的目录下,去生成一个唯一的临时有序节点, 然后判断自己是否是这些有序节点中序号最小的一个,如果是,则算是获取了锁。如果不是,则说明没有获取到锁,那么就需要在序列中找到比自己小的那个节点,并对其调用exist()方法,对其注册事件监听,当监听到这个节点被删除了,那就再去判断一次自己当初创建的节点是否变成了序列中最小的。如果是,则获取锁,如果不是,则重复上述步骤。当释放锁的时候,只需将这个临时节点删除即可

悲观锁、乐观锁以及分布式锁相关推荐

  1. 【分布式锁】三种分布式锁的实现【原创】

    分布式锁 0x00 概述 0x02 实现方式 0x03 分布式锁:基于数据库 1. 实现思想 A. 悲观锁(排他锁) B. 乐观锁 2. 优缺点 0x04 分布式锁:基于Zookeeper 1. 实现 ...

  2. zookeeper 分布式锁_关于redis分布式锁,zookeeper分布式锁原理的一些学习与思考

    编辑:业余草来源:https://www.xttblog.com/?p=4946 首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法 ...

  3. Redis 作者 Antirez 讲如何实现分布式锁?Redis 实现分布式锁天然的缺陷分析Redis分布式锁的正确使用姿势!...

    Redis分布式锁基本原理 采用 redis 实现分布式锁,主要是利用其单线程命令执行的特性,一般是 setnx, 只会有一个线程会执行成功,也就是只有一个线程能成功获取锁:看着很完美. 然而-- 看 ...

  4. 分布式锁系列--04关于分布式锁的选型分析02-Redlock的实现原理

    欢迎关注公众号:java4all 上一文分布式锁系列–03关于分布式锁的选型分析01中,我们看到了单节点的redis分布式锁在failover时产生了无法解决的安全问题,因此,Redis的作者anti ...

  5. 深入浅出 超详细 从 线程锁 到 redis 实现分布式锁(篇节 1)

    在 使用 redis 实现分布式锁 之前 我们需要先了解以下几点 什么是分布式锁 要介绍 什么是分布式锁,那首先要提到 与之对应的 的两个锁:线程锁 和 进程锁 1.线程锁 主要 用来 给方法.代码块 ...

  6. 什么是分布式锁?几种分布式锁分别是怎么实现的?

    一.什么是分布式锁: 1.什么是分布式锁: 分布式锁,即分布式系统中的锁.在单体应用中我们通过锁解决的是控制共享资源访问的问题,而分布式锁,就是解决了分布式系统中控制共享资源访问的问题.与单体应用不同 ...

  7. **Java有哪些悲观锁的实现_Redis 分布式锁的正确实现方式(Java版)

    前言 分布式锁一般有三种实现方式: 数据库乐观锁: 基于Redis的分布式锁: 基于ZooKeeper的分布式锁 本篇博客将介绍第二种方式,基于Redis实现分布式锁. 虽然网上已经有各种介绍Redi ...

  8. **Java有哪些悲观锁的实现_Redis 分布式锁的正确实现方式(Java 版)

    点击上方"Java基基",选择"设为星标" 做积极的人,而不是积极废人! 源码精品专栏 原创 | Java 2020 超神之路,很肝~ 中文详细注释的开源项目 ...

  9. redis分布式锁实现原理_redis分布式锁实现分析与实践

    前言: 在分布式环境中, 我们有些情况下需要使用到锁进行并发控制, 可供基于的 redis, zookeeper,mysql类数据库 基于数据库类的实现是乐观锁, 基于redis,zookeeper的 ...

  10. mysql 分布式锁_【分布式锁的演化】分布式锁居然还能用MySQL?

    前言 之前的文章中通过电商场景中秒杀的例子和大家分享了单体架构中锁的使用方式,但是现在很多应用系统都是相当庞大的,很多应用系统都是微服务的架构体系,那么在这种跨jvm的场景下,我们又该如何去解决并发. ...

最新文章

  1. nginx gzip配置
  2. MYSQL数据库——mysql的数据类型和运算符
  3. Quartz Properties 文件
  4. 观察者设计模式 php,PHP设计模式 - 观察者模式
  5. 【转】Ubuntu 16.04 安装 CUDA10.1 (解决循环登陆的问题)
  6. zookeeper安装和使用 windows环境
  7. 不同的写法 其中 1 2 (试了下 没有效果 ,先记载这里把)
  8. linux下生成静态库和动态库
  9. (12)FPGA时钟设计原则
  10. python下载论文_Python实现一个论文下载器的过程
  11. teigha开发从入门到精通(3)-- 编译drawings sdk示例
  12. 材料成型及控制工程学计算机吗,材料成型及控制工程 硕士以后 工资多少,
  13. 基于控制台的老虎机Java Demo
  14. Janitor Troubles
  15. 从mysql2ch到synch,一次重构与升级
  16. 我想知道如何用风扇自制水空调?
  17. java word 加密_java 加密解密WORD文档
  18. mysql自然连接的例题详解_基于 MySQL 的数据库实践(自然连接)
  19. create connection SQLException, url: jdbc:mysqlAccess denied for user ‘CC‘@‘localhost‘ (using passwo
  20. Joy Catalog

热门文章

  1. 【Servlet】什么情况下调用doGet和doPost
  2. PHP7新特性(常用)
  3. 用Python爬取手机APP
  4. c语言怎么返回数组,c++从函数返回数组的方法代码
  5. 进制转换对学计算机的意义,浅析进制转换在计算机应用中的技巧.docx
  6. 什么是工作流?(转贴)
  7. 如何学习一种开发框架
  8. 应用架构COLA 2.0
  9. 迷宫寻径问题(数据结构4.4.3)
  10. Julia:报错 no method matching increment_deriv!(::Float64, ::Float64)