Redis作为分布式锁存在的问题

148 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29天,点击查看活动详情

1 redis中死锁的现象

若是一个服务拿到锁之后,在执行业务逻辑的时候挂掉了,也就是没有执行释放锁的动作。那么这个锁就一直存在于redis中,其他用户来redis中加锁,则会一直失败。这种现象就称之为死锁

2 redis死锁解决办法

给锁设置过期时间

setnx key value ex 10s

在这种情况下,当拿到锁的服务挂掉之后,若是超过这个过期时间,锁也会被释放掉,其他服务也是可以拿到锁的。

还有可能这样操作:
setnx key value
exbire key 10s
这样也会给key设置过期时间,但是若是set命令执行完毕了,exbire命令执行之前,服务挂掉了,一样会有死锁的问题。
所以,设置key和给key设置过期时间必须是一个原子操作

3 redis过期时间引发的两个问题

3.1 程序还没有执行完,锁过期了

当程序A在redis中设置一个key的过期时间为10s,但是A程序执行时间共花费14S,当第十秒的时候,锁被自动释放,其程序B在redis中setnx成功,也就是拿到这个锁。而后4秒钟两端程序并行,也会有多线程并发的问题。

3.2 程序删除了别人加的锁

当程序A执行完之后,去redis中删除锁,这个时候,A自己设置的锁已经超时删除了,但是有一样的key存在,是B设置的,那么A也会把这个锁删除掉。这个时候,若是有程序C来拿锁,也会拿到锁,引起一系列的问题。

4 解决方案

4.1 程序还没有执行完,锁过期了

A程序拿到锁之后,给锁设定在一个过期时间比如为1min,然后去执行自己的业务逻辑。这个时候启动一个守护线程,这个线程的作用就是每当锁的过期时间剩余30s的时候,检查A程序是否还在正常运行,若是还在正常运行,则将锁的过期时间续期到1min。

4.2 程序删除了别人加的锁

设置key的value的时候,进行保存,当删除的时候,校验这个value值。是自己设置的再删除。这样就可以预防删除别人加的锁的问题。

5 分段锁提升redis锁的性能

在前边的讲解中我们可以设置锁并且加过期时间和校验value的值来加一个分布式锁。

但是若是1000万用户1000个商品,第一个用户拿到锁之后,执行下订单的逻辑,那么其他所有的用户都要等待拿锁。这样效率是很低的。

我们可以将1000个商品分为10组每组商品都可以设置一把锁。之前只有一个用户可以拿到锁并执行下单操作,这时就有10个用户可以拿到锁并下单。这样整体效率就提升了10倍。这就是分段锁

6 redis集群的问题

单节点redis若是发生故障,则会造成比较大的问题。一般都是集群部署的。

但若是A程序在master节点加完锁之后,尚未同步到slave节点,master节点挂了。这时候其中一个slave节点选举成为了新的master。但是并没有之前A程序设置的锁的信息,B程序来获取锁,是可以成功的。

这其实是redis之间数据的同步不是实时的导致的问题

7 redis红锁

启动5个redis,他们之间的数据不做同步。当加锁的业务进来之后,在3个(过半)redis设置了锁之后,才认为这个锁添加成功了。

8 红锁的问题

但这也有一个问题,当A程序在3个redis上加锁之后,其中一个加锁的redis挂掉了,之后又立马重启了一个新的redis。这个时候程序B来加锁,会在剩余的两台redis和这个新启动的redis上加锁成功,那么程序B也就获取到了锁,同样引发了之前超卖的问题。

这个问题的解决方案就是不要立即启动宕机的redis server,等待一段时间,等待锁被主动释放或者锁过期释放之后,再启动redis server。

9 redis终极问题

当一个程序A在redis上设置锁之后,启动守护线程给锁延期,但是当程序A运行的时候发生了STW,这个时候守护线程也是阻塞的,并不会延期。当锁过期之后,其他程序的也会获取到锁。这个问题再redis中无解。

但这个问题可以使用zookeeper作为分布式锁来解决。