分布式锁的必要性与应用场景
分布式锁解决多节点并发访问共享资源的一致性问题。典型场景包括:电商库存扣减防止超卖、定时任务防止重复执行、分布式环境下的幂等性保障、订单状态流转的互斥控制。单机环境可用synchronized或ReentrantLock解决,分布式架构下必须跨进程跨机器协调,需要外部存储作为锁状态的中介。
分布式锁的正确性要求:互斥性(同一时刻仅一个客户端持有锁)、无死锁(持有锁的客户端崩溃后锁能自动释放)、容错性(存储服务部分节点故障时锁服务仍可用)。
Redis单节点锁与常见陷阱分析
Redis单节点锁使用SET key value NX EX命令实现原子性加锁,是最简单也最常用的方案。
# Redis单节点加锁(原子操作)
SET lock:order:12345 unique_client_id NX EX 30
# 解锁(Lua脚本保证原子性)
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
// Java实现 - Redisson分布式锁
Config config = new Config();
config.useSingleServer().setAddress("redis://10.0.1.10:6379");
RedissonClient redisson = Redisson.create(config);
RLock lock = redisson.getLock("lock:order:12345");
try {
boolean acquired = lock.tryLock(5, 30, TimeUnit.SECONDS);
if (acquired) {
int stock = getStock(order.getItemId());
if (stock > 0) {
deductStock(order.getItemId(), 1);
createOrder(order);
}
}
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
单节点锁的陷阱:Redis主从切换时锁可能丢失(主节点加锁后未同步到从节点即宕机)、锁续期问题(业务执行时间超过过期时间导致锁提前释放)。Redisson的watchdog机制通过后台线程每10秒(默认租约1/3)续期来解决续期问题。
Redis RedLock算法多节点锁实现
RedLock是Redis作者Antirez提出的多节点分布式锁算法,使用N个独立Redis实例(通常5个),客户端向所有实例依次加锁,获得多数(N/2+1)成功且总耗时未超过锁有效期即视为加锁成功。算法解决了单节点故障导致锁丢失的问题。
// RedLock Java实现
RedissonClient client1 = Redisson.create(Config.fromYAML("redis1-config.yaml"));
RedissonClient client2 = Redisson.create(Config.fromYAML("redis2-config.yaml"));
RedissonClient client3 = Redisson.create(Config.fromYAML("redis3-config.yaml"));
RLock lock1 = client1.getLock("lock:order:12345");
RLock lock2 = client2.getLock("lock:order:12345");
RLock lock3 = client3.getLock("lock:order:12345");
RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
try {
boolean acquired = redLock.tryLock(5, 30, TimeUnit.SECONDS);
if (acquired) {
processOrder(order);
}
} finally {
redLock.unlock();
}
RedLock的关键参数:锁有效期TTL必须远大于获取所有实例锁的总耗时;时钟漂移是RedLock的理论弱点,实际部署中各节点使用NTP同步时钟可将偏差控制在毫秒级。
etcd分布式锁与对比选型建议
etcd基于Raft一致性协议实现分布式锁,天然保证强一致性和线性一致性,不存在RedLock的时钟漂移问题。适合对一致性要求高于性能的场景。
// etcd分布式锁实现(Go语言)
func acquireLock(client *clientv3.Client, key string, ttl int64) (leaseID int64, err error) {
lease, err := client.Grant(context.TODO(), ttl)
if err != nil {
return 0, err
}
txn := client.Txn(context.TODO())
txn.If(clientv3.Compare(clientv3.CreateRevision(key), "=", 0))
txn.Then(clientv3.OpPut(key, "locked", clientv3.WithLease(lease.ID)))
txn.Else(clientv3.OpGet(key))
resp, err := txn.Commit()
if err != nil {
client.Revoke(context.TODO(), lease.ID)
return 0, err
}
if !resp.Succeeded {
client.Revoke(context.TODO(), lease.ID)
return 0, fmt.Errorf("lock already held")
}
return int64(lease.ID), nil
}
选型建议:电商秒杀等高并发场景用Redis单节点锁+Redisson watchdog,性能最优;金融交易等强一致场景用etcd锁或ZooKeeper锁;跨数据中心场景用RedLock五节点方案。任何方案都需配合业务层幂等性设计,锁只是并发控制手段而非绝对保障。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/fen-bu-shi-suo-shi-xian-fang-an-dui-bi-yu-redisredlock-suan/