MST

星途 面试题库

面试题:缓存设计之高级难度:分布式缓存与本地缓存一致性问题处理

在一个既有分布式缓存又有本地缓存的系统架构中,数据更新时如何保证两者的一致性?请详细描述实现方案及可能遇到的问题和解决方案。
48.0万 热度难度
后端开发缓存设计

知识考点

AI 面试

面试题答案

一键面试

实现方案

  1. 先更新数据库,再更新缓存
    • 步骤:
      • 应用程序发起数据更新请求,首先更新数据库中的数据。
      • 接着同时更新分布式缓存和本地缓存中的对应数据。在更新分布式缓存时,可以使用缓存客户端提供的API,如Redis的SET命令来更新数据。对于本地缓存,也调用相应的更新方法将新数据放入缓存。
    • 优点:操作相对简单,符合一般的业务逻辑认知,先确保数据持久化,再更新缓存以保证数据一致性。
    • 缺点:在高并发场景下,如果多个更新请求同时到达,先更新数据库后更新缓存的操作可能会导致缓存短暂不一致。例如,请求A和请求B几乎同时更新数据,请求A先更新数据库,还未来得及更新缓存,请求B又更新了数据库,然后请求B更新缓存,此时请求A更新缓存的数据就被覆盖,可能导致缓存中的数据并非是最新的完整状态。
  2. 先删除缓存,再更新数据库
    • 步骤:
      • 应用程序收到数据更新请求,首先删除分布式缓存和本地缓存中的对应数据。可以使用缓存的删除API,如Redis的DEL命令。
      • 然后进行数据库的更新操作。
    • 优点:在一定程度上减少了缓存不一致的时间窗口。因为先删除缓存,后续读取数据时会从数据库加载最新数据并重新填充缓存,保证了缓存数据最终一致性。
    • 缺点:在高并发场景下可能出现“缓存击穿”问题。如果在删除缓存后,更新数据库前,大量请求同时查询该数据,这些请求会直接穿透缓存到达数据库,给数据库带来较大压力。
  3. 使用消息队列(MQ)
    • 步骤:
      • 应用程序发起数据更新请求,将更新操作封装成消息发送到消息队列,如Kafka、RabbitMQ等。
      • 消息队列消费者监听队列,接收到更新消息后,先更新数据库,再更新分布式缓存和本地缓存。
    • 优点:通过异步处理,解耦了数据更新和缓存更新操作,提高了系统的整体性能和稳定性。同时,消息队列可以对消息进行持久化,保证消息不会丢失,从而确保缓存更新的可靠性。
    • 缺点:引入了消息队列增加了系统的复杂性,需要处理消息的顺序性、重复消费、消息积压等问题。例如,如果消息处理顺序不当,可能导致缓存更新错误;若消费者处理消息速度过慢,可能出现消息积压,影响缓存更新的及时性。

可能遇到的问题及解决方案

  1. 缓存更新失败
    • 问题描述:在更新分布式缓存或本地缓存时,由于网络故障、缓存服务故障等原因,导致缓存更新操作失败,从而造成缓存与数据库数据不一致。
    • 解决方案
      • 重试机制:对缓存更新操作进行重试。可以设置一定的重试次数和重试间隔,如失败后立即重试一次,若仍失败,间隔1秒后再重试,最多重试3次。
      • 记录日志:详细记录缓存更新失败的日志,包括失败时间、更新的数据内容、失败原因等信息,方便后续排查问题。
      • 补偿机制:可以定期扫描数据库与缓存,对比数据,发现不一致时手动或自动触发缓存更新操作。
  2. 缓存击穿
    • 问题描述:如前文所述,在使用“先删除缓存,再更新数据库”方案时,在缓存失效瞬间,大量请求同时访问该数据,直接穿透缓存到数据库,可能导致数据库压力过大甚至崩溃。
    • 解决方案
      • 互斥锁:在查询数据库前,使用分布式锁(如Redis的SETNX命令实现),只允许一个请求去查询数据库并更新缓存,其他请求等待。当第一个请求更新完缓存后,释放锁,其他请求可以从缓存中获取数据。
      • 热点数据永不过期:对于热点数据设置永不过期,定期在后台异步更新缓存数据,这样可以避免因缓存过期导致的缓存击穿问题。
  3. 缓存雪崩
    • 问题描述:大量缓存数据在同一时间过期,导致大量请求同时穿透缓存到数据库,使数据库负载过高甚至宕机。
    • 解决方案
      • 随机过期时间:在设置缓存过期时间时,给每个缓存数据加上一个随机的过期时间偏移量,避免大量数据同时过期。例如,原本设置缓存过期时间为1小时,可以改为1小时加上0到10分钟的随机值。
      • 二级缓存:使用两层缓存结构,一级缓存设置较短的过期时间,二级缓存设置较长的过期时间。当一级缓存失效时,先从二级缓存获取数据,同时异步更新一级缓存,减轻数据库压力。
  4. 数据不一致时间窗口
    • 问题描述:无论采用哪种方案,在更新数据库和更新缓存之间总会存在一定的时间差,这段时间内可能出现数据不一致的情况。
    • 解决方案
      • 缩短更新操作时间:优化数据库更新和缓存更新的代码逻辑,减少单个操作的执行时间,从而缩短不一致时间窗口。
      • 缓存版本控制:为缓存数据增加版本号,每次数据更新时,版本号递增。在读取缓存数据时,同时获取版本号与数据库中的版本号进行对比,若不一致则重新从数据库加载数据并更新缓存。