如何保持Redis与mysql保持一致性?

70 阅读2分钟

延迟双删

再写库前后都进行redis del(key)操作,并且第二次删除通过延迟的方式进行。

方案一(一种思路,不严谨)具体步骤:

  1. 先删除缓存
  2. 再写数据库
  3. 休眠500毫秒(根据具体的业务时间来定)
  4. 再次删除缓存

那么,这个500毫秒怎么确定的,具体该休眠多久呢?

需要评估自己的项目的读数据业务逻辑的耗时。这么做的目的,即使确保读请求结束后,写请求可以删除读请求造成的缓存脏数据。

当然,这种策略还要考虑redis和数据库主从同步的耗时。最后的写数据的休眠时间:则在读数据业务逻辑的耗时的基础上,加上几百ms即可。比如:休眠1秒。

方案二,异步延迟删除:

  1. 先删除缓存
  2. 再写数据库
  3. 触发异步写入串行化mq(也可以采用一种key+version的分布式锁)
  4. my接受再次删除缓存

异步删除对线上业务无影响,串行化处理保证并发情况下正确删除。

双删失败如何处理?

1.设置缓存过期时间

从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。所有的写操作以数据库为准,只要达到缓存过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存。 结合双删策略+缓存超时设置,这样最差的情况就是再超时时间内数据存在不一致。

2.重试方案

重试方案有两种实现,一种再业务层做,另外一种实现中间件负责处理

业务层实现重试如下:

image.png

  1. 更新数据库数据
  2. 缓存因为种种问题删除失败
  3. 将需要删除的key发送至消息队列
  4. 自己消费消息,获得需要删除的key
  5. 继续重试删除操作,直到成功

然而,该方案有一个缺点,对业务线代码造成大量的侵入。于是有了方案二,再方案二中,启动一个订阅程序去订阅数据库的binlog,获得需要操作的数据。在应用程序中,另启一段程序,获得这个订阅程序传来的信息,进行删除缓存 操作。

中间件实现重试  如下:

image.png

  1. 更新数据库数据
  2. 数据库会将操作信息写入binlog日志中
  3. 订阅程序提取出所需要的数据以及key
  4. 另起一段非业务代码,获得该信息
  5. 尝试删除缓存操作,发现删除失败
  6. 将这些信息发送至消息队列
  7. 重新从消息队列中获得该数据,重试操作