1. 分布式锁的前世今生
第一次接触分布式锁是在2016年做电商秒杀系统时。当时我们的促销活动上线后,出现了严重的超卖问题——100件库存的商品竟然卖出了120多件。排查发现,在集群环境下多个节点的服务实例同时判断"库存>0"并执行扣减,导致并发问题。这就是典型的分布式环境下的资源竞争场景,也是分布式锁要解决的核心问题。
分布式锁本质上是一种跨进程、跨机器的互斥机制。与单机环境下的synchronized或ReentrantLock不同,它需要在网络不可靠的分布式系统中,确保同一时刻只有一个客户端能获取锁。这就像多人协作编辑文档时,谁先获取编辑权谁就能修改内容,其他人必须等待。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Redis实现分布式锁的核心原理
2.1 SETNX命令的妙用
Redis的SETNX(SET if Not eXists)命令是实现锁的基础。当键不存在时设置值并返回1,键已存在则返回0。这个原子性操作完美契合了锁的"争抢"特性:
bash复制SETNX lock_key unique_value
但单纯使用SETNX会存在死锁风险——如果客户端获取锁后崩溃,锁将永远无法释放。因此我们通常需要设置过期时间:
bash复制SET lock_key unique_value NX PX 30000
这条命令在Redis 2.6.12后支持,实现了"设置值+过期时间"的原子操作。其中NX表示仅当键不存在时设置,PX指定毫秒级过期时间。
2.2 解锁的安全隐患
解锁操作看似简单却暗藏玄机。直接使用DEL命令删除锁键会导致严重问题:
bash复制# 错误示范!
DEL lock_key
假设客户端A获取锁后执行时间超过过期时间,锁自动释放。此时客户端B获取了锁,然后客户端A执行完调用DEL,就会误删客户端B的锁。正确的解锁姿势应该是:
lua复制if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
这段Lua脚本通过验证unique_value来确保只有锁的持有者才能释放锁。在Redis中执行Lua脚本是原子性的,完美解决了并发安全问题。
