延迟双删
再写库前后都进行redis del(key)操作,并且第二次删除通过延迟的方式进行。
方案一(一种思路,不严谨)具体步骤:
- 先删除缓存
- 再写数据库
- 休眠500毫秒(根据具体的业务时间来定)
- 再次删除缓存
那么,这个500毫秒怎么确定的,具体该休眠多久呢?
需要评估自己的项目的读数据业务逻辑的耗时。这么做的目的,即使确保读请求结束后,写请求可以删除读请求造成的缓存脏数据。
当然,这种策略还要考虑redis和数据库主从同步的耗时。最后的写数据的休眠时间:则在读数据业务逻辑的耗时的基础上,加上几百ms即可。比如:休眠1秒。
方案二,异步延迟删除:
- 先删除缓存
- 再写数据库
- 触发异步写入串行化mq(也可以采用一种key+version的分布式锁)
- my接受再次删除缓存
异步删除对线上业务无影响,串行化处理保证并发情况下正确删除。
双删失败如何处理?
1.设置缓存过期时间
从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。所有的写操作以数据库为准,只要达到缓存过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存。 结合双删策略+缓存超时设置,这样最差的情况就是再超时时间内数据存在不一致。
2.重试方案
重试方案有两种实现,一种再业务层做,另外一种实现中间件负责处理
业务层实现重试如下:
- 更新数据库数据
- 缓存因为种种问题删除失败
- 将需要删除的key发送至消息队列
- 自己消费消息,获得需要删除的key
- 继续重试删除操作,直到成功
然而,该方案有一个缺点,对业务线代码造成大量的侵入。于是有了方案二,再方案二中,启动一个订阅程序去订阅数据库的binlog,获得需要操作的数据。在应用程序中,另启一段程序,获得这个订阅程序传来的信息,进行删除缓存 操作。
中间件实现重试 如下:
- 更新数据库数据
- 数据库会将操作信息写入binlog日志中
- 订阅程序提取出所需要的数据以及key
- 另起一段非业务代码,获得该信息
- 尝试删除缓存操作,发现删除失败
- 将这些信息发送至消息队列
- 重新从消息队列中获得该数据,重试操作