51mee - AI智能招聘平台Logo
模拟面试题目大全招聘中心会员专区

设计一个银行系统中使用的分布式锁,需要考虑高并发、高可用、避免死锁等问题。

招商银行信息技术类岗位难度:中等

答案

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) 【追问清单】

  1. 如何处理锁超时后未释放的情况?
    回答要点:通过设置合理的锁超时时间(如10秒),并配合监控告警机制,当检测到锁未释放时,自动触发释放逻辑(如超时重试或手动释放)。
  2. 如何避免死锁?
    回答要点:采用加锁顺序(银行系统按业务逻辑顺序加锁,如先读后写),避免循环等待;同时设置锁超时时间,防止锁占用时间过长导致死锁。
  3. 如果Redis节点故障,如何保证锁的可用性?
    回答要点:采用红锁策略(在多个Redis实例上尝试加锁),即使单个实例故障,其他实例仍能正常加锁;同时保证Redis集群的高可用性(如主从复制、哨兵模式)。
  4. 如果业务逻辑复杂,需要多级锁(如读锁和写锁),如何设计?
    回答要点:可以扩展为读写锁,读锁允许多个并发读取,写锁独占,通过Redis的多个key(如read_lock、write_lock)实现,并保证加锁顺序(先读后写)。
  5. 锁的粒度如何选择?是否越细越好?
    回答要点:锁的粒度需平衡并发性和数据一致性,过细会导致并发降低,过粗可能导致数据不一致。银行系统中,应根据业务场景选择合适的锁粒度(如按账户ID加锁,而非整个系统)。

7) 【常见坑/雷区】

  1. 忽略原子性导致竞态条件:直接使用SET命令加锁,未结合Lua脚本保证原子性,可能导致多个节点同时获取锁。
  2. 超时时间设置不当:超时时间太短会导致频繁重试,增加系统压力;太长会导致锁占用时间过长,影响其他节点获取锁。
  3. 未考虑分布式环境下的网络延迟:未设置合理的重试间隔,可能导致死锁或超时错误。
  4. 死锁问题:未保证加锁顺序(如先读后写),导致循环等待死锁。
  5. 单点故障:未采用红锁策略,依赖单个Redis实例,导致节点故障时锁服务不可用。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1