面试题答案
一键面试优化锁的获取流程
- 减少锁粒度:
- 将大的业务操作拆分成多个小的操作,对每个小操作加锁。例如,在电商库存扣减场景中,如果涉及多个商品库存的修改,可以为每个商品单独加锁,而不是对整个订单的所有商品库存修改加一把大锁,这样可以降低锁竞争的概率。
- 优化重试机制:
- 当获取锁失败时,不要立即重试,可以采用随机退避策略。例如,在第一次获取锁失败后,等待一个随机的时间(如10 - 100毫秒之间的随机值)再重试,避免大量客户端同时重试造成更激烈的锁竞争。同时,设置最大重试次数,防止无限重试占用过多资源。
- 使用乐观锁:
- 对于一些读多写少的场景,可以考虑使用乐观锁。在Redis中,可以利用
watch
命令结合事务来实现乐观锁。例如,在更新数据前,先watch
该数据的键,在执行事务时,如果数据在watch
之后没有被其他客户端修改,则事务执行成功;否则,事务回滚。这种方式不需要在整个操作过程中一直持有锁,减少了锁竞争。
- 对于一些读多写少的场景,可以考虑使用乐观锁。在Redis中,可以利用
优化锁的释放流程
- 确保原子性释放:
- 使用Lua脚本来释放锁,以保证释放锁操作的原子性。因为在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]
是获取锁时设置的唯一标识(如UUID),只有当锁的值与获取锁时设置的值一致时才释放锁,防止误释放其他客户端的锁。 - 设置过期回调:
- 可以使用Redis的发布订阅功能来设置过期回调。当锁过期时,通过发布一个消息通知相关客户端,让它们重新获取锁并进行相应的处理。这样可以避免因为锁过期而导致的业务逻辑异常。
处理锁超时导致的数据一致性问题
- 合理设置锁超时时间:
- 根据业务操作的平均耗时来合理设置锁超时时间。例如,经过压测发现某个业务操作在99%的情况下能在10秒内完成,那么可以将锁的超时时间设置为15秒左右,既要保证业务操作有足够的时间执行,又不能设置过长导致其他客户端等待时间过长。
- 续期机制:
- 引入锁续期机制,例如使用Redisson的看门狗机制。在获取锁后,启动一个后台线程,定期检查业务操作是否完成,如果未完成则延长锁的过期时间。例如,每5秒检查一次,若业务操作还在进行,则将锁的过期时间再延长10秒。
- 数据补偿机制:
- 当出现锁超时导致数据不一致的情况时,建立数据补偿机制。可以记录操作日志,在业务操作完成后,通过对比操作日志和实际数据状态来发现不一致的地方,并进行补偿操作。例如,在库存扣减场景中,记录每次扣减的数量和时间,定期检查库存数量是否与记录一致,若不一致则根据日志进行调整。