Redis分布式锁

用C++编写的Redishttps://github.com/sewenew/redis-plus-plus目录

Redis分布式锁的实现核心思路

Redis分布式锁误删问题

解决方案:

分布式锁的原子性问题

 解决问题:Lua脚本解决多条命令原子性

解决重入问题

解决不可重入问题

解决主从一致性问题 


分布式锁:满足分布式系统集群模式多进程可见并且互斥的锁

 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路

Redis分布式锁的实现核心思路

实现分布式锁时需要实现的两个基本方法:

  •  获取锁:

    • 互斥:确保只能有一个线程获取锁

    • 非阻塞:尝试一次,成功返回true,失败返回false

#添加锁,NX保证互斥性,EX设置超时时间
SET lock thread1 NX EX 10
  • 释放锁:

    • 手动释放

    • 超时释放:获取锁时添加一个超时时间

#释放锁,删除即可
DEL key

Redis分布式锁误删问题

持有锁的线程在锁的内部出现了阻塞,导致他的锁自动释放,这时其他线程,线程2来尝试获得锁,就拿到了这把锁,然后线程2在持有锁执行过程中,线程1反应过来,继续执行,而线程1执行过程中,走到了删除锁逻辑,此时就会把本应该属于线程2的锁进行删除,这就是误删别人锁的情况说明

解决方案:

在获取锁时存入线程标示(可以用UUID表示) 在释放锁时先获取锁中的线程标示,判断是否与当前线程标示一致

  • 如果一致则释放锁

  • 如果不一致则不释放锁

分布式锁的原子性问题

线程1现在持有锁之后,在执行业务逻辑过程中,他正准备删除锁,而且已经走到了条件判断的过程中,比如他已经拿到了当前这把锁确实是属于他自己的,正准备删除锁,但是此时他的锁到期了,那么此时线程2进来,但是线程1他会接着往后执行,当他卡顿结束后,他直接就会执行删除锁那行代码,相当于条件判断并没有起到作用,这就是删锁时的原子性问题,之所以有这个问题,是因为线程1的拿锁,比锁,删锁,实际上并不是原子性的。

 解决问题:Lua脚本解决多条命令原子性

Redis提供了Lua脚本功能,在一个脚本中编写多条Redis命令,确保多条命令执行时的原子性。

LUA基本语法https://www.runoob.com/lua/lua-tutorial.htmlRedis提供的调用函数如下:

redis.call('命令名称', 'key', '其它参数', ...)

Redis命令调用Lua脚本

#调用脚本
EVAL "return redis.call('set', KEYS[1], ARGV[1])" 1 name Rose

 最终我们操作redis的拿锁比锁删锁的lua脚本

-- 这里的 KEYS[1] 就是锁的key,这里的ARGV[1] 就是当前线程标示
-- 获取锁中的标示,判断是否与当前线程标示一致
if (redis.call('GET', KEYS[1]) == ARGV[1]) then
  -- 一致,则删除锁
  return redis.call('DEL', KEYS[1])
end
-- 不一致,则直接返回
return 0

解决重入问题

重入问题:获得锁的线程可以再次进入到相同的锁的代码块中,可重入锁的意义在于防止解决方案:判断线程id,并利用hash类型,记录重入的次数

开始往redis里边去写数据 ,写成一个hash结构

redis.call('hset', KEYS[1], ARGV[2], 1)

如果当前这把锁存在,则第一个条件不满足,再判断

redis.call('hexists', KEYS[1], ARGV[2]) == 1

此时需要通过大key+小key判断当前这把锁是否是属于自己的,如果是自己的,则进行

redis.call('hincrby', KEYS[1], ARGV[2], 1)

解决不可重试问题

不可重试:是指目前的分布式只能尝试一次。通常该情况是不合理的。
合理的情况是:当线程在获得锁失败后,他应该能再次尝试获得锁。

解决主从一致性问题 

主从一致性: 如果Redis提供了主从集群,当我们向集群写数据时,主机需要异步的将数据同步给从机,而万一在同步过去之前,主机宕机了,互斥锁失效,此时新的 Redis 主节点依然可以获取锁,所以多个应用服务就可以同时获取到锁。

解决方案: Redlock

Redlock 算法的基本思路,是让客户端和多个独立的 Redis 节点依次请求申请加锁,如果客户端能够和半数以上的节点成功地完成加锁操作,那么我们就认为,客户端成功地获得分布式锁,否则加锁失败。(上面链接C++中支持红锁)

Redlock 算法加锁三个过程:

  • 第一步是,客户端获取当前时间(t1)。
  • 第二步是,客户端按顺序依次向 N 个 Redis 节点执行加锁操作:
    • 加锁操作使用 SET 命令,带上 NX,EX/PX 选项,以及带上客户端的唯一标识。
    • 如果某个 Redis 节点发生故障了,为了保证在这种情况下,Redlock 算法能够继续运行,我们需要给「加锁操作」设置一个超时时间(不是对「锁」设置超时时间,而是对「加锁操作」设置超时时间),加锁操作的超时时间需要远远地小于锁的过期时间,一般也就是设置为几十毫秒。
  • 第三步是,一旦客户端从超过半数(大于等于 N/2+1)的 Redis 节点上成功获取到了锁,就再次获取当前时间(t2),然后计算计算整个加锁过程的总耗时(t2-t1)。如果 t2-t1 < 锁的过期时间,此时,认为客户端加锁成功,否则认为加锁失败。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值