redis分布式锁

65 阅读4分钟

什么是分布式锁

分布式锁就是在分布式环境下,多台机器上,对共享资源进行访问,加上的锁,就是分布式锁

分布式锁的特性

互斥性

锁的目的就是为了限制多个竞争者对共享资源的访问,同一时刻只能允许一个竞争者访问共享资源,所以一定要保证互斥性。

安全性

分布式锁的设置一定要设定过期时间,用来兜底释放锁,能够在设置分布式锁后,即使单台主机发生崩溃,其他竞争者也能够获取到分布式锁。避免了设置锁的竞争者在设置锁以后发生崩溃而无法释放锁,造成其他竞争者也无法对共享资源进行访问。

对称性

需要对分布式锁设定一个唯一标识,标识该分布式锁是由哪个竞争者设置的,谁设置的锁就应该由谁进行释放。防止其他竞争者对别人设置的锁进行释放。

可靠性

需要有异常处理能力和容灾能力

接下来我们看一下redis是如何保证上述这四种特性的

如何使用分布式锁

分布式锁的使用分为加锁解锁两个步骤

加分布式锁有两种方式

  1. setNX key value (简单版本)
  2. set key value nx ex/px seconds

setNX

简单版本加锁

如上图所示,key如果不存在,加锁成功,返回1,key如果存在,加锁失败,返回0。 删除锁,使用delete命令,对锁进行删除即可。

存在的问题

setNX这种简化的版本有个问题就是没有设置过期时间,如果设置锁的客户端设置完锁之后宕机了,那么这个锁就得不到释放,会一直占用系统资源。

解决办法

redis提供了另一个命令,可以对分布式锁单独增加过期时间

  1. expire key seconds
  2. setex key seconds value

新问题

上述两条命令可以设置分布式锁并设置锁的过期时间,但是这两个操作不是原子性操作,会造成加锁之后客户端宕机没有办法加过期时间,还是无法释放锁。

set key value nx ex/px

redis提供了该命令,以满足设置分布式锁并增加过期时间的原子性操作。

原子性操作

上图中就实现了设置锁和过期时间的原子性操作。 同时,redis加分布式锁的原则遵循谁加的锁谁释放,可以把value设置为客户端的id,比如上图中,set lock 1 nx ex 3 1就是客户端的id,lock这个锁只能由id为1的客户端解锁。

存在的问题

解决了原子性加锁问题,那么解锁其实还存在问题,在进行解锁的时候,需要先检查解锁的客户端是否是加锁的客户端,然后才能解锁。这两个操作就需要保证原子性,redis没有提供原子性解锁的命令,怎么办?不用慌,我们可以使用lua脚本,来保证解锁的原子性。

if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
else
    return 0
end
  1. 检查锁:使用 redis.call("get", KEYS[1]) 检查指定的key(锁)当前的值是否与提供的值(ARGV[1])相等。这个值通常是一个唯一标识符,比如客户端ID或者一个随机生成的字符串,用来确保只有锁的持有者可以释放锁。
  2. 释放锁:如果值匹配,使用 redis.call("del", KEYS[1]) 删除这个key,从而释放锁。
  3. 返回结果:如果成功删除key,则脚本返回删除操作的结果(通常为1,表示成功删除了一个元素)。如果条件不匹配,返回0,表示锁没有被释放。

至此,通过原子性加锁命令,设置客户端id,lua脚本解锁命令,就保证了redis的互斥性安全性对称性

可靠性保证

  1. 主从数据同步,实现容灾(具体同步详情可参考这篇文章)

主从同步

  1. 哨兵机制(待补充)
  2. 多机部署

多机部署

总结

本文介绍了什么是分布式锁?分布式锁具有哪些特性?如何利用redis进行分布式锁的加锁和解锁操作,以及最后简单介绍分布式锁的可靠性如何保证。

本文由mdnice多平台发布