早上跟面试阿里云的学长沟通了一下,其中一个面试题如下(不一定是标准答案,只是按照我的理解整理了一波)
- 针对先删除缓存再更新数据库的情况:
- 使用延时双删:
- 基本思路: 在写库前后都进行删除缓存操作,并且设置合理的超时时间
- 基本步骤: 先删除缓存–再写数据库—休眠一段时间—再次删除缓存
- 注:休眠的时间是根据自己的项目的读数据业务逻辑的耗时来确定的。这样做主要是为了保证在写请求之前确保读请求结束,写请求可以删除读请求造成的缓存脏数据。
- 该方案的弊端: 集合双删策略+缓存超时策略设置,这样最差的结果就是在超时时间内数据存在不一致,又增加了写请求的耗时。
- 异步延迟删除:
- 基本步骤:先删除缓存-再写数据库-触发异步写人串行化mq(也可以采取一种key+version的分布式锁)-mq接受再次删除缓存。
- 比单纯的延时删除的优点:异步删除对线上业务无影响,串行化处理保障并发情况下正确删除。
- 为什么要双删?
- db更新分为两个阶段,更新前及更新后,更新前的删除很容易理解,在db更新的过程中由于读取的操作存在并发可能,会出现缓存重新写入数据,这时就需要更新后的删除。
- 双删除的缓存失败则使用最下面提到的策略
- 针对缓存删除失败的情况:
- 先更新 数据库,然后直接删除 cache。
- 如果更新数据库成功,但删除缓存失败,则有如下的解决方案:
- 缩短缓存失效时间(不常用) :
- 缩短缓存过期时间,这样能缩短缓存与数据库不一致的时间,且此方法只能用于先操作数据库,再操作缓存的情况;
- 增加 cache 更新重试机制(常用):
- 当因cache服务不可用导致的缓存失败时,可以设置隔一段时间进行重试,重试次数可自定义,若多次重试仍不可用,则将当前更新失败的 key存入队列,待cache服务可用后对应删除缓存中的key即可 ;
- 重试方案有两种实现,一种在业务层做,另外一种实现中间件负责处理。
- 业务层实现重试如下:
- 整体流程:
- 具体步骤:
- 更新数据库数据;
- 缓存因为种种问题删除失败;
- 将需要删除的key发送至消息队列;
- 自己消费消息,获得需要删除的key;
- 继续重试删除操作,直到成功。
- 存在的缺点:
- 会对业务代码造成大量的入侵;
- 中间件实现重试:
- 整体流程:
- 具体步骤:
- 更新数据库数据;
- 数据库会将操作信息写入binlog日志当中;
- 订阅程序提取出所需要的数据以及key;
- 另起一段非业务代码,获得该信息;
- 尝试删除缓存操作,发现删除失败;
- 将这些信息发送至消息队列;
- 重新从消息队列中获得该数据,重试操作。
- 启动一个订阅程序去订阅数据库的binlog,获得需要操作的数据。在应用程序中,另起一段程序,获得这个订阅程序传来的信息,进行删除缓存操作。