Redis与MySQL如何保证一致性

247 阅读4分钟

一、缓存不一致如何发生

1.1 数据修改,缓存写回策略

  • 同步直写策略:写缓存时,同步写数据库
  • 异步写回策略:写缓存时不同步写数据库,等到数据从缓存中淘汰,再写回数据库。如果缓存故障,会造成数据丢失 综上,如果要保证缓存和数据库数据一致,采用同步直写,但是由于这两部分的操作是分开的,如果要保证一致性那么需要在应用中使用事务机制,两者要么一起更新要么都不更新;失败之后需要进行重试,重试失败则需要告警。
    在对数据一致性要求不是那么高,则可以使用异步写回策略。

1.2 数据修改过程有一方失败,数据一致性问题

新增数据

对于新增数据,数据会直接写到数据库,不需要操作缓存,可以保证缓存和数据库数据的一致性

删除数据

删除数据时,既要更新数据库,也要删除缓存中的数据。如果两个操作无法保证原子性,就会出现一致性问题
假设应用先删除缓存,再更新数据库,如果缓存删除成功数据库更新失败,那么应用再访问数据时,会将数据库中的旧值读取出来,应用访问的是旧值
假设数据库先完成了更新,但是缓存删除失败,那么如果有其他并发请求访问数据,也会读到旧值

1.3 并发情况下,数据库一致性问题

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

线程A删除缓存后,在未来得及更新数据库的情况下,线程B开始读取数据,线程B发现缓存数据缺失,从数据库中读取旧值放入缓存,导致线程B读取的是旧值,由于线程B将旧值放入缓存导致其他线程读取到的也是旧值。当线程A完成数据库更新,那么此时缓存中是旧值,数据库是新值,造成不一致。

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

线程A更新数据库到新值,但是未来得及删除缓存值,线程B开始读取数据,缓存命中读取旧值,但由于线程A更新缓存操作比较快,其他线程再读取时发现缓存缺失,会从数据库中读取新值,影响较小

二、如何解决

2.1 数据修改过程有一方失败一致性问题解决

重试机制:将要删除的缓存值或要更新的数据库值暂存在消息队列中。当应用删除缓存或更新数据库失败时,重新读取消息队列的值,再次尝试。如果删除成功,那么将这些值从消息队列中移除。在重试达到一定次数,向业务层告警

2.2 并发情况下一致性问题解决

方式一 先更新数据库,再删除缓存 方式二 先删除缓存,再更新数据库,在数据库更新完成之后先sleep一段时间,再删一次缓存(延时双删),sleep的时间需要大于线程B读取数据再写入缓存的时间

三、在数据更新时,为什么需要删除缓存而不是更新缓存

3.1 更新缓存带来的问题

假设有线程A、B同时需要更新数据

  1. 线程A发起写操作,第一步先更新数据库
  2. 线程B发起写操作,在A之后更新数据库
  3. 由于网络原因,B先更新缓存
  4. A更新缓存 上述情况下,会导致A的老数据存入了缓存,B的新数据存入了数据库,导致数据不一致
    使用删除缓存时,则不会出现脏数据(顶多再把缓存删一遍)

3.2 更新缓存的劣势

  • 对于需要复杂计算的缓存值,更新频率高的话,会浪费性能
  • 在写多读少的场景下,可能数据更新频率大于读取频率,浪费性能