Redis与MySQL缓存双写一致性问题

先来看一些常见的面试题

读写缓存

我们的读写缓存分为两种情况

  • 同步直写缓存策略:完成第二部立即回写Redis
  • 异步缓写缓存策略:不急立即写回Redis
    • 正常业务运行中,MySQL数据变动了,但是可以在业务上容许出现一定时间后才作用于Redis,比如仓库、物流系统
    • 异常情况出现了,不得不将失败的动作重新修补,有可能需要借助kafka或者RabbitMQ等消息中间件,实现重试重写

那么代码应该怎么写呢?下面是初级版本,业务逻辑并没有写错,对于小厂中厂(QPS<=1000)可以使用,但是大厂不行

但是以上代码有问题,如果大量请求进行第一个查询Redis为空,那么后续判断都会进行MySQL查询,打高MySQL的IO

双检加锁策略

那么如何解决上述问题呢?

多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它。其他线程走到这一步就等着,等第一个线程查询到了数据,然后做缓存。后面的线程进来发现已经有缓存了,就直接走缓存

双重检查锁思路一样,值得借鉴

数据库和缓存一致性的几种更新策略

给缓存设置过期时间,定期清理缓存并回写,是保证最终一致性的解决方案。

我们可以对存入缓存的数据设置过期时间,所有的写操作以MySQL数据库为准,对缓存操作只是尽最大努力,如果数据库写入成功,缓存更新失败,那么只要达到过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存,达到一致性,切记,要以MySQL的数据库写入为准

  • 先更新数据库,再更新缓存
  • 先更新缓存,再更新数据库
  • 先删除缓存,再更新数据库
  • 先更新数据库,再删除缓存

先更新数据库,再更新缓存

异常情况1:

异常情况2:

先更新缓存,再更新数据库

不推荐,业务上一般把MySQL作为底单数据库,保证最后解释

先删除缓存,再更新数据库

为了解决这个问题,所有有了延时双删

延时双删

优化版:

先更新数据库,再删除缓存(推荐)

异常情况:

虽然这个也有问题,但是是大厂比较推荐的方法

canal结合binlog

最终方案选择

总结

canal


Redis与MySQL缓存双写一致性问题
http://example.com/2023/12/26/Redis与MySQL缓存双写一致性问题/
Author
Posted on
December 26, 2023
Licensed under