Redis分布式锁学习记录

Redis分布式锁学习记录

一、为什么要用分布式锁

首先我们要明确一个定义,分布式锁是为了保证统一时间只有一个客户端能对共享资源进行操作(update、delete、insert)。
在涉及到一些订单、库存、金额时,为了避免出现超卖、重复扣款等情况发生,我们必须要保证从下单到扣款一系列的操作都只能成功执行一次,而在一般的正式生产环境中为了保证服务的高可用性我们会采用服务集群的模式,这也就直接宣布@Synchronized、Lock这些方法没办法有效地保证集群服务之间的数据一致性,分布式事务锁也就由此诞生。

二、分布式锁的实现

分布式锁可以有多种实现方式,比如通过Mysql数据库、Redies等就能有多种实现方式。

1.通过唯一索引或主键索引,达到互斥锁的效果

把一把锁的id作为主键插入到数据库,如果锁已存在,其他人就会插入失败,也就抢不到锁,在业务逻辑完成或者超时后再把这条数据删掉。

2.通过Mysql的乐观锁,实现数据一致性的效果

当完成一个update或者delete命令时,再重新查一遍数据看看是否与预期数据一样,如果和预期数据一样这轮操作就算成功完成,如果不一样就要进行事务回滚,但是这种方法显然难以避免在高并发情况下产生脏读的情况。

3.通过Redis实现

用Mysql固然可以实现分布式锁,但是在高并发的场景下效率显然是比较容易出现性能瓶颈的,因此我们最好的选择还是通过Redis来实现。

1)抢锁

用Redis的Setnx命令去抢锁,当一个请求成功抢到锁后,其他请求就只能进入等待或者直接返回失败

2)死锁

当一个请求抢到锁后还会存在一个问题也就是如果业务逻辑由于数据库、第三方接口等种种原因导致无限超时,那这把锁就会变成死锁,因此我们要用expire命令给这把锁加一个过期时间,过期时间比必须大于业务正常执行所需要的时间,保证即使锁拥有者不主动释放锁也不会产生死锁的结果。

3)看门狗

当一个请求抢到锁后由于种种原因导致业务执行时间超出正常预期(只超出几秒或者其他可控范围内)时,如果锁由于自动过期时间直接被释放,这时第二个请求抢到这把锁开始执行业务,执行业务期间上一个得到锁的线程执行释放锁的动作,把第二个请求得到的锁释放了,这就产生锁错乱的结果。这时候我们要引入一个看门狗的概念,其作用就是对请求一这样小超时但是不至于到死锁的级别时进行一个锁续时的操作,看门狗默认有效时间是30s,也就是说30s就不会再进行续时操作了,同时在finally代码块中写上释放锁的动作,进一步保证锁的安全和有效性。

4)分布式Redis节点

经过以上几个步骤的逐步完善,在大部分情况下我们已经能在分布式锁的作用下保证数据的安全性和一致性,但是在极端情况下譬如Redis节点崩了,那就直接造成缓存穿透、缓存血崩等等情况,分布式锁自然也就失效了。这时候我们就可以考虑用多台(单数)Redis单节点来保证redis的高可用,譬如用3台单节点Redis服务,每个请求要在这3个节点中抢到至少2把锁才算是抢锁成功,这样即使暂时一台redis节点宕机了也不会直接造成数据错乱等后果。

5)延迟启动Redis节点

假设现在有3台Redis单机节点,当其中一台Redis节点宕机后,已经抢到锁的请求在业务执行完毕后会释放掉已经抢到的2个锁,其中一个锁的Redis节点已宕机,也就是释放另一个锁,此时第二个请求只抢到一把锁,在这之后拿到第二把锁,抢锁成功,业务继续执行。如果在第一个请求释放锁之前直接把宕机的Redis节点重启,那第二个请求就会直接拿到重启的Redis节点的锁,开始执行业务,就会出现两个请求同时执行业务的情况,也就是锁失效了。因此我们要至少确保一轮业务跑完,释放掉锁之后再重启宕机的Redis节点。

三、通过阿里开源组件Seata实现强一致性的分布式锁

阿里开源组件Seata在进行配置后可以用一个@GlobaTranscational注解保证这个业务类下所有方法都能具有数据强一致性,也就是所有步骤都结束后和预期结果对比没有报错,再统一对数据库进行操作。相比于前面用Redis就算弱一致性,报错后回滚是分开回滚的。由此不难判断Seata虽然能保证数据的强一致性,但是效率也是比较低的,在对于金融操作如转账等我们可以用Seata来处理,对于一些库存的加减我们可以用效率更高的Redis分布式锁来处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为了测试 Redis 分布式锁,你可以按照以下步骤进行操作: 1. 确保你已经安装了 Redis,并且可以通过命令行或者其他方式连接到 Redis 服务器。 2. 创建一个测试程序或者脚本,用于模拟多个并发请求获取和释放 Redis 分布式锁的情况。 3. 在测试程序中,使用 Redis 客户端库连接到 Redis 服务器。 4. 在测试程序中,创建多个并发线程或者进程,每个线程或进程都会尝试获取 Redis 锁。 5. 在每个线程或进程中,使用 Redis 的 SETNX 命令尝试获取锁。如果 SETNX 返回 1,表示获得了锁;如果返回 0,表示锁已经被其他线程或进程持有。 6. 如果获取到锁,执行需要加锁的业务逻辑;如果未获取到锁,等待一段时间后再次尝试获取锁。 7. 在业务逻辑执行完毕后,使用 Redis 的 DEL 命令释放锁。 8. 在测试程序中,记录每个线程或进程获取和释放锁的情况,以及锁的持有时间和并发冲突情况。 9. 执行测试程序,观察输出结果,检查是否存在并发冲突和锁的正确获取和释放。 10. 根据测试结果进行分析和调优,修改测试程序或者业务逻辑,以确保 Redis 分布式锁的正确性和高可用性。 请注意,在测试分布式锁时,你可能需要考虑以下情况: - 并发请求的数量和频率 - 锁的超时时间和自动释放机制 - 锁的重入性和可重入性 - 异常情况下的锁的释放和恢复机制 - 锁的可靠性和高可用性 以上是一般的测试步骤,你可以根据具体的使用场景和需求进行适当调整和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值