redis实现分布式锁

1.redis分布式锁

redis可以使用setnx命令实现分布式锁。
setnx的意思是如果不存在则设置值。通过设置一个key value作为锁。当这个key存在的时候,就说明加锁了,当这个key不存在的时候,就说明没加锁,那就设置key value进行上锁。

> setnx lock true
OK
... do something critical ...
> del lock
(integer) 1

2. setnx存在的问题(expire命令)

假如在上锁之后,指定代码逻辑过程中这台机器宕机挂掉了,导致锁一值存在没有释放,由于是分布式环境,那么其他服务器一致抢占不到锁,导致死锁,

2.1 解决办法

可以加上一个锁的过期时间,在指定的时间之内如果锁没释放的话,就会自动释放锁。
可以使用redis提供的expire命令。

> setnx lock true
OK
> expire lock 5
... do something critical ...
> del lock
(integer) 1

3.expire命令存在的问题(使用setnx key value ex 时间 nx)

由于setnx和expire是两条命令,并不是原子操作,还是存在在执行完setnx命令之后,服务器挂掉导致没执行expire命令的隐患。这时可以使用redis提供的一个原子操作,将setnx和expire变成一个原子操作命令

> set lock true ex 5 nx
 OK
 ... do something critical ... 
> del lock

4.redis实现的分布式并不能解决超时问题

如果加锁和解锁之间的代码逻辑执行过久,导致超出了锁的过期时间,那么会导致锁被释放,然后其他机器拿key进行加锁,然后再其他机器执行过程中,第一台超时导致锁被释放的机器代码执行完了,然后执行del key操作,把其他机器拿到的锁给释放了,存在线程不安全问题。

一个解决思路是,设置的key为一串随机数,然后在释放锁的时候匹配随机数是否一致,一致再删除key。但是匹配和删除并不是一个原子操作。这时候就需要使用Lua脚本来处理了,因为Lua脚本可以保证连续多个指令的原子执行

tag = random.nextint() # 随机数
if redis.set(key, tag, nx=True, ex=5):
 do_something()
 redis.delifequals(key, tag) # 假象的 delifequals 指令
 
# delifequals
if redis.call("get",KEYS[1]) == ARGV[1] then
 return redis.call("del",KEYS[1])

posted on 2021-10-20 23:44  帅哥川  阅读(142)  评论(0编辑  收藏  举报

导航