一、分布式锁的定义
分布式锁(Distributed Lock)是分布式系统或不同系统实例之间用于控制对共享资源访问的一种同步机制。在分布式环境中,由于多个进程或线程可能运行在不同的机器上,传统的单机锁(如Java中的synchronized关键字或ReentrantLock)无法有效地工作,因为它们只能控制单个JVM内的同步。分布式锁的核心目标是确保在分布式系统中的多个客户端之间,对共享资源的访问是互斥的,即同一时间只有一个客户端能够访问该资源。
二、分布式锁的特征
互斥性:这是分布式锁最基本的特性。它确保在任何时候,只有一个客户端(或进程、线程)能持有同一个锁。这保证了共享资源在分布式系统中的互斥访问。
可重入性:同一个客户端在持有锁的情况下,可以多次获取该锁而不会导致死锁。这通常通过维护一个计数器来实现,每次获取锁时递增,每次释放锁时递减。
锁超时:分布式锁通常具有超时机制,以防止死锁的发生。如果客户端在持有锁后的一定时间内没有释放锁(可能是由于客户端崩溃或其他原因),锁将自动释放,允许其他客户端获取。
高性能和高可用:加锁和解锁需要开销尽可能低,同时也要保证高可用,避免分布式锁失效(高性能:查询快,高可用:节点故障时,服务仍然能正常运行或进行降级后提供部分服务)。
并发正确性:确保在同一时间只有一个线程或进程能够访问某个资源,从而避免数据竞争和不一致性问题。
三、 Redis分布式锁的实现
1.获取锁
使用 SETNX 命令尝试将锁的值设置为当前客户端的唯一标识符(通常是一个 UUID 或其他唯一字符串)。如果键不存在,SETNX 会成功并返回 1,表示获得锁;如果键已存在,SETNX 会失败并返回 0,表示锁已被其他客户端持有。
基本语法:
SETNX key value
示例步骤:客户端A请求服务器设置key的值,如果设置成功就表示加锁成功,客户端B也去请求服务器设置key的值,如果返回失败,那么就代表加锁失败,客户端A执行代码完成,删除锁客户端B在等待一段时间后再去请求设置key的值,设置成功,客户端B执行代码完成,删除锁(如图一所示)

图一
2.设置锁的过期时间
为了防止客户端崩溃或网络问题导致锁无法释放,需要为锁设置一个过期时间(TTL)。这可以通过 EXPIRE 命令来实现。
ps:SETNX 和 EXPIRE 是两个独立的命令,不是原子操作。为了确保这两个操作的原子性,Redis 提供了 SET 命令的扩展选项 NX 和 EX,可以在一个命令中同时完成设置键值和设置过期时间。
3. 释放锁
释放锁时,需要确保只有持有锁的客户端才能删除锁。这通常通过比较锁的值来实现,
使用 LUA 脚本来确保原子性,因为 LUA 脚本在 Redis 中是原子执行的。
手动释放,直接删除即可
del key
四、常见问题
1.如果获取锁成功后服务宕机,永远不会释放怎么办?
解决方案:超时释放,获取锁时添加一个超时时间(需要利用expire命令给锁设置一个有效期)
SETNX lock thread1 #尝试获取锁
EXPIRE lock 10 #设置有效期
2.如果获取锁成功,expire之前服务宕机怎么办?
解决方案:要保证setnx和expire命令的原子性,redis的set命令可以满足,需要添加nx和ex的选项:
NX:与setnx一致,第一次执行成功
EX:设置过期时间
set key value [EX time秒] [NX]
set lock thread1 EX 10 NX
1218

被折叠的 条评论
为什么被折叠?



