问题:当数据库有数据更新时,怎样保证redis缓存中的数据与数据库数据一致?
Redis更新的正确方法
看到好些人在写更新缓存数据代码时,先删除缓存,然后再更新数据库,而后续的操作会把数据再装载的缓存中。然而,这个是逻辑是错误的。试想,两个并发操作,一个是更新操作,另一个是查询操作,更新操作删除缓存后,查询操作没有命中缓存,先把老数据读出来后放到缓存中,然后更新操作更新了数据库。于是,在缓存中的数据还是老的数据,导致缓存中的数据是脏的,而且还一直这样脏下去了。
视图分析:
写流程(更新策略)
1.先淘汰 cache(删除缓存);
2.再写 DB(更新数据库)。
读流程
1.先读 cache,如果数据命中 hit 则返回;
2.如果数据未命中 miss 则读 DB;
3.将 DB 中读取出来的数据入缓存。
什么情况下可能出现缓存和数据库不一致呢?
在分布式环境下,数据的读写都是并发的,上游有多个应用,通过一个服务的多个部署(为了保证可用性,一定是部署多份的),对同一个数据进行读写,在数据库层面并发的读写并不能保证完成顺序,也就是说后发出的读请求很可能先完成(读出脏数据):
1.发生了写请求 A,A 的第一步淘汰了 cache(如上图中的1);
2.A 的第二步写数据库,发出修改请求(如上图中的2);
3.发生了读请求 B,B 的第一步读取 cache,发现 cache 中是空的(如上图中的步骤3);
4.B 的第二步读取数据库,发出读取请求,此时 A 的第二步写数据还没开始,读出了一个脏数据放入 cache(如上图中的步骤4)。
最终导致缓存与数据库不一致。
总结
先更新数据库,后删除缓存(建议,使用场景最多),但是,是不是这个就不会有并发问题了?不是的,比如,一个是读操作,但是没有命中缓存,然后就到数据库中取数据,此时来了一个写操作,写完数据库后,让缓存失效,然后,之前的那个读操作再把老的数据放进去,所以,会造成脏数据。