一、缓存不一致如何发生
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同时需要更新数据
- 线程A发起写操作,第一步先更新数据库
- 线程B发起写操作,在A之后更新数据库
- 由于网络原因,B先更新缓存
- A更新缓存
上述情况下,会导致A的老数据存入了缓存,B的新数据存入了数据库,导致数据不一致
使用删除缓存时,则不会出现脏数据(顶多再把缓存删一遍)
3.2 更新缓存的劣势
- 对于需要复杂计算的缓存值,更新频率高的话,会浪费性能
- 在写多读少的场景下,可能数据更新频率大于读取频率,浪费性能