如何保证mysql和redis数据一致性的问题

如何保证mysql和redis数据一致性的问题

  • 问题是什么
  • 三种常用的缓存读写策略
  • 旁路缓存策略
    • 策略是什么
    • 思考一、为什么是先更新DB,再删除Cache
    • 思考二、为什么是删除Cache,可以更新吗
    • 缺点
  • 读写穿透策略
    • 策略是什么
    • 思考一、和旁路模式有什么区别
  • 异步缓存写入

问题是什么

现在的后台架构中,经常使用redis作为缓存,使用mysql作为数据库,以实现更加高效的读写。然而在高并发的情况下,有时会出现redis和mysql数据不一致的情况,例如数据已经更新到了mysql数据库,缓存中读取到的仍然是旧数据。
为了缓解这种情况的出现,我们要针对不同的场景设计不通的缓存策略,例如读多写少的场景,读少写多的场景,对数据一致性要求高的场景,对时效性要求高的场景等,没有一个能够以一敌万的策略,只有更加合适,出错率更低的策略。

三种常用的缓存读写策略

旁路缓存模式策略、读写穿透策略、异步缓存写入策略。

旁路缓存策略

策略是什么

写数据:

  1. 直接写入DB,然后删除缓存

读数据:

  1. 如果缓存命中,直接返回结果。
  2. 缓存没有命中,则读取DB,更新缓存,返回结果。

思考一、为什么是先更新DB,再删除Cache

流程很简单,为什么是先更新DB,再删除Cache,可以交换下顺序吗?答案是不行的;
如果先删除cache,再更新DB,可能会造成缓存和DB的数据不一致,例如:

写请求 读请求 读请求
删除Cache数据A
查询Cache数据A不存在
从DB读取数据A
更新数据A到Cache
更新数据B到DB
查询Cache数据,读取到A

当多个请求并发时,写请求1的两个操作之间穿插了请求2的所有操作(主要是写DB比较慢,需要分配更多的时间片才能执行完成),导致Cache的数据没有正确更新。只有等下次更新或者缓存自动过期后才会把最新的数据B存入缓存,如果是更新则有可能再次发生这样的问题,导致一直不一致…

那么按照先更新DB,再删除Cache的方式就一定没有问题吗,理论上还是会有问题:

读请求 写请求 读请求
查询Cache,不存在
从DB读取数据A
更新数据B到DB
删除Cache数据A
更新数据A到Cache
查询Cache,读取到数据A

同样可能由于线程调度等原因,读请求 1 的更新Cache操作晚于写请求 2 中的删除Cache操作,这样会导致最终写入缓存中的是来自请求 1 的旧值A,而写入数据库中的是来自请求 2 的新值B,即缓存数据落后于数据库,此时再有读请求 3 命中缓存,读取到的便是旧值A。
但与之前不同的是,这种场景出现的概率要小许多,因为更新DB所需的线程调度时间要远大于更新Cache,所以一般情况下都是Cache先执行完成。

思考二、为什么是删除Cache,可以更新吗

这里主要考虑一个性能问题,缓存的更新可能成本很高,例如设计多表联合计算,复杂运算等,在写频繁的时候性能会下降严重。

缺点

  1. 首次请求的数据一定不在cache中,这样对于一些新上的热点数据,就会对数据库造成比较大的压力,甚至冲坏数据库。对于这种情况,我们可以将热点数据提前写入cache中。
  2. 写操作比较频繁的时候,cache中的数据被频繁的删除,这样会影响缓存的命中率。对于这种情况,说明这种方法可能不太适合,应该及时调整架构,更换缓存策略。

读写穿透策略

策略是什么

写数据:

  1. 先查cache,cache中不存在,cache provider服务直接更新db;
  2. 先查cache,cache中存在,cache providr服务同时更新db和缓存;

读数据:

  1. 先查cache,命中直接返回;
  2. 先查cache,没有命中,则从DB加载到cache中,再返回响应;

思考一、和旁路模式有什么区别

旁路策略是客户端自己负责把数据写入到cache中,而读写穿透策略增加了一个cache provider服务,该服务负责将数据写入缓存、DB、读取数据等,这种方法会让代码更简洁,同时减少客户端的负载。

异步缓存写入

写数据:

  1. 直接将数据写入缓存
  2. 通过批量异步的方式,按照一定策略定期更新数据库;

读数据:

  1. 先查cache,命中直接返回;
  2. 先查cache,没有命中,则从DB加载到cache中,再返回响应;

这种方式下,缓存和数据库的一致性不强,对一致性要求高的系统要谨慎使用。但是它适合频繁写的场景,MySQL的InnoDB Buffer Pool机制就使用到这种模式。

你可能感兴趣的:(redis,mysql,缓存)