
1) 【一句话结论】采用基于Redis的分布式锁方案,通过Lua脚本保证原子性,结合超时机制和红锁策略,兼顾高并发、高可用与死锁避免。
2) 【原理/概念讲解】老师口吻,解释分布式锁的核心需求——在分布式系统中,多个节点需对共享资源(如银行账户余额)进行互斥操作,防止数据不一致。银行系统对锁的可靠性要求极高,因此需满足:1. 高并发:支持大量并发请求快速获取/释放锁;2. 高可用:节点故障时锁服务仍可用;3. 避免死锁:确保锁获取/释放逻辑无死循环。类比:分布式锁就像银行系统的“共享锁头”,多个ATM(节点)都想用这个锁头操作账户(共享资源),只有拿到锁的ATM才能操作,其他ATM等待,这样保证账户余额操作的一致性。
3) 【对比与适用场景】
| 方案 | 定义 | 特性 | 使用场景 | 注意点 |
|---|---|---|---|---|
| Redis分布式锁 | 基于Redis的SETNX命令实现,通过原子性操作加锁 | 原子性(SETNX)、可超时(EX)、简单高效 | 银行交易系统(如转账)、中小并发场景 | 需保证Redis高可用,避免单点故障;超时时间需合理设置 |
| ZooKeeper分布式锁 | 基于Znode的临时节点,通过watcher机制实现 | 顺序性(保证锁获取顺序)、可扩展 | 复杂锁结构(如多级锁)、高可用场景 | 性能略低于Redis,适合复杂业务 |
| Consul分布式锁 | 基于Consul的KV存储,通过CAS操作实现 | 原子性(CAS)、可重入 | 微服务架构(如服务间通信)、动态环境 | 需依赖Consul集群,配置复杂 |
4) 【示例】伪代码示例(Redis实现):
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
lock_key = "account_balance_lock"
lock_value = str(time.time())
# 尝试加锁,设置超时时间10秒
result = r.set(lock_key, lock_value, ex=10, nx=True)
if result:
print("获取锁成功")
# 执行业务逻辑(如扣款)
# ...
# 释放锁
r.delete(lock_key)
else:
print("获取锁失败,等待重试")
# 在多个Redis实例上尝试加锁
redis_clients = [redis.Redis(host='r1'), redis.Redis(host='r2'), redis.Redis(host='r3')]
lock_key = "account_balance_lock"
lock_value = str(time.time())
for client in redis_clients:
result = client.set(lock_key, lock_value, ex=10, nx=True)
if result:
# 成功获取锁,退出循环
break
if result:
# 执行业务逻辑
# ...
# 释放锁(在所有实例上删除)
for client in redis_clients:
client.delete(lock_key)
5) 【面试口播版答案】面试官您好,针对银行系统分布式锁的设计,我核心思路是采用Redis实现分布式锁,通过Lua脚本保证原子性,结合超时机制和红锁策略,兼顾高并发、高可用与死锁避免。具体来说,分布式锁的核心需求是在分布式系统中保证共享资源操作的互斥性,银行系统对数据一致性要求极高,因此锁的实现需满足:1. 高并发:支持大量并发请求快速获取/释放锁,避免请求堆积;2. 高可用:节点故障时锁服务仍可用,不会因单点故障导致业务中断;3. 避免死锁:确保锁获取/释放逻辑无死循环,比如通过加锁顺序(银行系统按业务逻辑顺序加锁,如先读后写)和超时机制(设置合理的锁超时时间,防止锁占用时间过长)实现。实现上,我们采用Redis的SET命令(带NX和EX参数)结合Lua脚本,保证加锁操作的原子性,防止竞态条件。同时,为应对Redis单点故障,采用红锁策略——在多个Redis实例上尝试加锁,只有全部成功才认为获取锁成功,释放锁时也在所有实例上删除,确保锁的一致性。这种方案既保证了分布式锁的可靠性,又兼顾了银行系统的高并发和高可用需求。
6) 【追问清单】
7) 【常见坑/雷区】