Redis分布式锁的原理是什么?如何续期?

redis如何防止并发?

Redis是当前炙手可热的NoSQL数据库,几乎已经成为高并发、高可用系统的标配了。对Redis响应快的认知不能仅仅停留在基于内存和单线程的层面。

创新互联公司是专业的固始网站建设公司,固始接单;提供网站设计、做网站,网页设计,网站设计,建网站,PHP网站建设等专业做网站服务;采用PHP框架,可快速的进行固始网站开发网页制作和功能扩展;专业做搜索引擎喜爱的网站,专业的做网站团队,希望更多企业前来合作!

在一些对高并发请求有限制的系统或者功能里,比如说秒杀活动,或者一些网站返回的当前用户过多,请稍后尝试。这些都是通过对同一时刻请求数量进行了限制,一般用作对后台系统的保护,防止系统因为过大的流量冲击而崩溃。对于系统崩溃带来的后果,显然还是拒绝一部分请求更能被维护者所接受。

而在各种限流中,除了系统自身设计的带锁机制的计数器外,利用Redis实现显然是一种既高效安全又便捷方便的方式。

客户端加锁(ReentrantLock或synchronized)

但此方式只限于单机加锁,无法解决分布式系统的并发竞争问题。

乐观锁(redis 的命令 watch)
当执行多键值事务操作时,Redis 不仅要求这些键值需要落在同一个 Redis 实例上,还要求落在同一个 slot 上,所以 redis 的事务比较鸡肋,不过可以想办法遵循 redis 内部的分片算法把设计到的所有 key 分到同一个 slot。

redis 的 setnx 实现分布式锁
要设置超时时间,防止抢占到锁的客户端因失败、崩溃或其他原因没有办法释放锁而造成死锁。

如有不同观点,欢迎发表评论。如果喜欢我的回答,欢迎“点赞、分享”。

1. 问题描述

并发竞争key这个问题简单讲就是:

同时有多个客户端去set一个key。

示例场景 1

例如有多个请求一起去对某个商品减库存,通常操作流程是:

假设当前库存值为 20,现在有2个连接都要减 5,结果库存值应该是 10 才对,但存在下面这种情况:

示例场景 2

比如有3个请求有序的修改某个key,按正常顺序的话,数据版本应该是 1->2->3,最后应该是 3。

但如果第二个请求由于网络原因迟到了,数据版本就变为了 1->3->2,最后值为 2,出问题了。

redis 本身是单进程、单线程的模型,就是说一个时刻就只能有一个东东在执行,不管是多少个命令,只能是串行执行,因此从这个意义上保证了单个命令执行的多线程(多个客户端操作)的安全,也就是不管有多少个客户端在发请求,redis每次只能执行一个客户端的命令,不存在多线程。

但是redis对于多个客户端的多个命令,并不能保证其线程安全性,比如有一个值x=1;如果ClientA 获取x的值,x=x+1,然后再设置回去;在此期间,有一个ClientB做同样的操作;如果ClientA、ClientB的操作被串行了,那么x=3;但是多个命令之间不能保证(除非是增加了所谓的锁之类的东东),从而x的值就不一定是3了,这个时候就存在了并发操作的问题。

当然redis也考虑到了相关的情况,提供了incr之类的原子操作命令,保证了多线程并发操作的安全性;

对于同一个客户的多次点击操作,如果不做区分,可能就存在问题,比如支付宝的种树浇水这个操作,如果不做控制,一个用户快速的多次点击,可能就会超过3次(支付宝限制一天只能帮某个好友浇水3次),这个时候其实有简单的解决方法:比如每次浇水有一个浇水ID,第1、2、3次都有一个不同的ID,从第4次开始的操作其ID还是设置为3(由客户端来限制),那么后台只要判断ID是否重复,就可以做过滤; 同样的问题在第三方API对接的时候也存在(比如调用支付宝付款,有时候网络不好,是否会存在多次付费问题?),此时我想每个支付的请求也带了一个唯一的ID,保证了支付的唯一性(当然可能还会有对用户名、支付款项的验证--银行支付的时候往往会提醒你,你支付了一笔同样的款项,需要确认--具体的场景是你需要支付某人1000块,你分成500、500两笔,此时网银操作是会提醒你的);

因此对于相同的2个请求,如果是一种幂等的操作--比如都是get某个值(不会变的),那么其实处不处理看策略,至少不会造成不一致;对于支付这样的行为,肯定需要做判断确定是同一个请求,对另外一个请求做过滤。

对于很多系统而言,都有集群处理。在集群中使用quartz或者task处理任务的时候,一般有三种选择:

1. 多实例顺序执行

2. 单实例执行

3. 多实例并行执行

创保目前考虑的是1情况。

redis对于并发较小的情况下,进行锁控制,效果是较为理想的。目前创保只有八台实例,适用于该方案。

1. 首先,redis提供了一个setnx方法,该方法执行后会返回key值在设置之前是否存在。

这是进行并发控制的基础,但并不够。

2. 在利用 setnx 进行 key 值设定后,如果instance发生异常(不仅仅是exception)会导致该锁无法正常释放。

所以,我们需要为锁设置一个失效时间,即expire命令。

Redis分布式锁的原理是什么?如何续期?

在传统单体应用单机部署的情况下,并发问题可以通过使用Java并发相关的锁如synchronized,但是当规模上升到分布式集群的情况下,要控制共享资源访问,就需要通过分布式锁来实现。常见的分布式锁方案如数据库乐观锁,Redis锁,zk锁等。

Redis分布式锁可以有多种方式实现但是其核心就是通过以下三个Redis命令组合实现。

上面为Redis的一个最简单的锁实现原理,实际中还需要考虑更多具体的情况作出相应的调整。如

实际开发环境中不确定的因素有很多,需要慢慢地去调整实践达到理想状态,可以考虑使用redisson框架来实现。

这个情况比较独特,出现这个问题的根本原因在于锁失效的时间小于业务处理的时间导致业务还没处理完毕锁就释放了。那么解决方案是合理地结合业务去设置锁失效的时间。

但是也有更好的方案就如前文提到的redisson,其中的可重入锁概念。


默认情况下,加锁的时间是30秒.如果加锁的业务没有执行完,那么到 30-10 = 20秒的时候,就会进行一次续期,把锁重置成30秒。

分布式锁的需求产生

分布式锁的需求是伴随着应用分布式部署而来的,在单体应用,且只部署一台服务器的情况下,通过java的同步锁即可实现。同步锁,即是一个原子性的操作。

那么当应用进行了分布式部署,应用有多个服务,这个时候应用服务端就没有一个可提供原子性操作的地方了,Redis性能高,且是单线程,因此可提供一个原子性操作的地方,利用它,就可以实现分布式锁。

用场景说话,使用Redis分布式锁的场景如下图所示:

如下图所示,随后会根据场景说明分布式锁及续期相关问题的来龙去脉。

  1. 图中序号1:进来一个请求,这个请求要求我们保存一个“订单A”;
  2. 图中序号2:2.1 步,请求进来,首先去尝试设置一个Redis 值,他的键就是订单号“订单A”,如果尝试成功,则代表我这个线程是第一次设置,相当于我拿到了这个锁;如果尝试失败,那么,可以抛出异常或者等待一段时候后再次重试,这里可以根据业务场景的不同采取不同的策略。这里的关键是在Redis中的操作是单线程的,因此该操作是原子性的。2.2步,为了防止应用服务意外中断,Redis中的数据一直存活,消耗资源,需要设置一个超时时间。(如果为了严谨,可以将2.1, 2.2 两步封装成一个lua脚本部署在Redis服务器上)
  3. 图中序号3:情况A,这个时候是当Redis的key还未失效,程序就已经执行完成,且删除了Redis中的数据,一切正常;情况B:就是需要续期的场景,如果要避免这个场景的出现,可以将Redis key的失效时间设置长一点,可以应对大多数业务。如果要彻底解决,可以在应用端添加一个Redis锁的注册中心,然后起一个监听线程去监听这个注册中心,发现有锁还在被持有,但是Redis 已经快过期了,则修改相应key的失效时间,进行续期。

到此,以上就是小编对于redis 并发锁的问题就介绍到这了,希望这2点解答对大家有用。

分享题目:Redis分布式锁的原理是什么?如何续期?
文章位置:http://www.shufengxianlan.com/qtweb/news47/105897.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联