什么是分布式锁
分布式锁就是在分布式环境下,多台机器上,对共享资源进行访问,加上的锁,就是分布式锁
分布式锁的特性
互斥性
锁的目的就是为了限制多个竞争者对共享资源的访问,同一时刻只能允许一个竞争者访问共享资源,所以一定要保证互斥性。
安全性
分布式锁的设置一定要设定过期时间,用来兜底释放锁,能够在设置分布式锁后,即使单台主机发生崩溃,其他竞争者也能够获取到分布式锁。避免了设置锁的竞争者在设置锁以后发生崩溃而无法释放锁,造成其他竞争者也无法对共享资源进行访问。
对称性
需要对分布式锁设定一个唯一标识,标识该分布式锁是由哪个竞争者设置的,谁设置的锁就应该由谁进行释放。防止其他竞争者对别人设置的锁进行释放。
可靠性
需要有异常处理能力和容灾能力
接下来我们看一下redis是如何保证上述这四种特性的
如何使用分布式锁
分布式锁的使用分为加锁和解锁两个步骤
加分布式锁有两种方式
- setNX key value (简单版本)
- set key value nx ex/px seconds
setNX

如上图所示,key如果不存在,加锁成功,返回1,key如果存在,加锁失败,返回0。 删除锁,使用delete命令,对锁进行删除即可。
存在的问题
setNX这种简化的版本有个问题就是没有设置过期时间,如果设置锁的客户端设置完锁之后宕机了,那么这个锁就得不到释放,会一直占用系统资源。
解决办法
redis提供了另一个命令,可以对分布式锁单独增加过期时间
- expire key seconds
- 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
- 检查锁:使用 redis.call("get", KEYS[1]) 检查指定的key(锁)当前的值是否与提供的值(ARGV[1])相等。这个值通常是一个唯一标识符,比如客户端ID或者一个随机生成的字符串,用来确保只有锁的持有者可以释放锁。
- 释放锁:如果值匹配,使用 redis.call("del", KEYS[1]) 删除这个key,从而释放锁。
- 返回结果:如果成功删除key,则脚本返回删除操作的结果(通常为1,表示成功删除了一个元素)。如果条件不匹配,返回0,表示锁没有被释放。
至此,通过原子性加锁命令,设置客户端id,lua脚本解锁命令,就保证了redis的互斥性、安全性和对称性。
可靠性保证
- 主从数据同步,实现容灾(具体同步详情可参考这篇文章)

- 哨兵机制(待补充)
- 多机部署

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