面试题答案
一键面试方案设计
- 锁的获取
- 基于ZooKeeper:在每个数据中心部署ZooKeeper集群。客户端尝试在ZooKeeper上创建一个临时顺序节点来获取锁。例如,创建节点路径为
/lock/node_
,ZooKeeper会自动为其分配一个唯一的顺序号。客户端获取节点列表,判断自己创建的节点是否是序号最小的节点。如果是,则获取到锁;否则,监听序号比自己小的前一个节点。当监听到前一个节点删除时,再次检查自己是否是最小序号节点,若是则获取锁。 - 基于Redis:利用Redis的
SETNX
(SET if Not eXists)命令来尝试获取锁。例如,SETNX lock_key unique_value
,其中unique_value
可以是客户端的唯一标识。在跨数据中心场景下,为了保证数据一致性,可以使用Redis的多数据中心同步机制,如Redis Cluster的跨数据中心部署或Redis Sentinel结合多个数据中心的主从复制。客户端在尝试获取锁时,向多个数据中心的Redis实例发送SETNX
命令,只有当在大多数数据中心都成功设置锁时,才认为获取到锁。
- 基于ZooKeeper:在每个数据中心部署ZooKeeper集群。客户端尝试在ZooKeeper上创建一个临时顺序节点来获取锁。例如,创建节点路径为
- 锁的释放
- 基于ZooKeeper:当客户端完成任务后,直接删除自己创建的临时节点。ZooKeeper会自动通知监听该节点变化的其他客户端。
- 基于Redis:客户端使用Lua脚本来释放锁,以确保释放锁的操作是原子性的。例如,Lua脚本如下:
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
其中KEYS[1]
是锁的键,ARGV[1]
是客户端获取锁时设置的唯一值。同样,释放锁的操作需要发送到多个数据中心的Redis实例,确保锁在所有相关数据中心都被正确释放。
3. 异常处理
- 基于ZooKeeper:如果客户端在持有锁期间发生崩溃,ZooKeeper会自动删除其创建的临时节点,从而其他客户端可以获取锁。对于网络分区等异常情况,ZooKeeper的选举机制可以保证在分区恢复后,系统能重新达到一致状态。
- 基于Redis:如果客户端在持有锁期间崩溃,需要设置锁的过期时间来防止死锁。例如,在获取锁时使用SET lock_key unique_value EX expire_time
,其中expire_time
是锁的过期时间。对于网络分区,Redis Cluster或Sentinel机制可以在分区恢复后重新调整节点状态,确保数据一致性。
优缺点
- 基于ZooKeeper的方案
- 优点:
- 强一致性保证,ZooKeeper的原子广播协议保证了数据的一致性,适合对数据一致性要求极高的场景。
- 天然支持分布式锁的监听机制,通过节点监听可以有效减少无效的锁竞争。
- 对于异常处理较为健壮,临时节点机制可以自动处理客户端崩溃等情况。
- 缺点:
- 性能相对较低,ZooKeeper主要用于协调服务,其写性能不如Redis,在高并发场景下可能成为瓶颈。
- 部署和维护成本较高,需要搭建和管理ZooKeeper集群。
- 优点:
- 基于Redis的方案
- 优点:
- 高性能,Redis是内存数据库,读写性能非常高,适合高并发场景。
- 部署和使用相对简单,Redis的使用方式较为灵活,且有丰富的客户端库支持。
- 缺点:
- 数据一致性相对较弱,虽然可以通过多数据中心同步机制提高一致性,但与ZooKeeper相比,在极端情况下仍可能出现数据不一致。
- 异常处理依赖于过期时间设置,可能会导致在过期时间内锁被其他客户端误获取的情况。
- 优点:
适用场景
- 基于ZooKeeper的方案:适用于对数据一致性要求极高,并发量相对不是特别大,对异常处理和可靠性要求较高的场景,如金融交易系统、分布式事务协调等。
- 基于Redis的方案:适用于高并发场景,对一致性要求相对没那么严格,更注重系统性能和响应速度的场景,如电商的库存扣减、抢购等业务场景。