分布式锁之Redis实现

2020-05-25  本文已影响0人  ronly

分布式锁

单机环境下,为解决多线程访问共享资源产生的线程安全问题,引入了锁机制。分布式环境下,多个服务实例也存在对共享资源的并发访问,此时需要使用分布式锁,协调不同服务实例对共享资源的访问。

分布式互斥锁实现方案

数据库中存储锁记录,加锁时使用select * from table where lock_name = 'xxx' for update增加排它锁。InnoDB的行级锁是加在索引上的。为避免锁表及减小加锁粒度,最好在lock_name列增加唯一索引。

基于Redis的分布式锁

注意事项:

Redis 分布式锁的工作流程:

image

1. 加锁与解锁

1.1 使用setnx命令(set if not exist)加锁


>setnx lock true

>expire lock 5

... do something critical ...

>del lock

存在的问题:

sentnx 与expire 是两条命令,不是原子的。若客户端A加锁成功,尚未设置锁过期时间时异常退出了。则锁一直得不到释放,将导致死锁。

将setnx和expire放到同一个Redis事务中执行的思路是行不通的。expire 依赖于 setnx的执行结果。若setnx未执行成功,expire不应该执行。事务中不存在if-else分支逻辑。

1.2 改进方案

Redis 2.8版本中,作者对set指令进行了扩展,使得setnx 和 expire可以一起执行。只有key设置成功后才会设置key的过期时间。


>set lock  true ex 5 nx

... do something critical

> del lock

1.3 超时问题

若临界区代码执行时间较长,以至于超出了锁的过期时间限制。锁过期后,第二个线程可以重新持有这把锁。若此时第一个线程执行完临界区代码,会将第二个线程持有的锁释放掉。其他线程可以在第二个线程临界区代码执行结束前获取到锁,线程不安全。

解决方案:为set指令的value参数设置一个随机数,解锁时先判断线程持有的随机数与value值是否一致。一致时才允许线程解锁。可以使用Lua脚本来保证value匹配与删除key两个指令的原子性。

上一篇 下一篇

猜你喜欢

热点阅读