生产中Redis的使用-缓存更新策略

302 阅读3分钟

生产中常常将一些查询操作放在redis缓存里而不是数据库,以提升接口的响应速度。一般做法是这样:先查询缓存,缓存命中直接返回,否则就查询数据库,没有值则直接返回,有值则先存入缓存,并设置过期时间,再返回数据。将更新缓存的操作放在了查询中,而不是写入操作(增删改接口)中。当数据发生变化(增删改)时,是先更新数据库,再删除缓存,待查询时再放入缓存中,并设置过期时间,是一种延时加载的策略,用到的时候再查询数据库放入缓存中。没有什么方案是完美的,结合业务权衡利弊下选择一种风险和损失相对较小的方案即可。

1、查询接口流程:

image.png 说明: 在更新redis这步操作时,要为缓存设置一个过期时间,这样做可以减少一些风险发生的机率,比如在写入操作更新了数据但是更新缓存失败或者由于线程并行访问问题导致缓存中的是旧数据,那么设置一个过期时间可以在写少读多的场景中让缓存自动失效,从而达到缓存数据和数据库数据的最终一致性。

2、写入接口流程:

写入流程就是操作数据库与缓存,为避免这样的双写操作带来的数据不一致问题,我们需要保证双写的原子性。单机版本的写入操作的原子性可以通过事务(@Transactional)来解决,如果是分布式服务就需要通过分布式事务的方式来让操作数据库和操作缓存要么成功要么失败。保证了原子性后就需要考虑操作数据库与操作缓存的顺序了,一般我们采用的做法有两种:第一种:先删缓存,再更新数据库;第二种:先更新数据库再删除缓存。其它做法也有好多:比如延时双删,即先删除缓存再更新数据库然后等待一个时间(大概就是一个查询的时间:查数据库+更新缓存)。还有基于消息队列的基于binlog的方式等等,结合业务即可。另外为什么是删缓存而不是更新,原因就是更新操作并发条件下容易出现更新顺序不一致的情况,以及更新操作很频繁的话不仅io多,且对于查询来说只看最后的结果,相当于做了很多无效操作,不如删除来的收益高。

2.1、先删除缓存再更新数据库:

这个方案有个很明显的缺点就是删除缓存的操作很快,但是更新数据库的操作就相对很慢了,如果先删除缓存,此时刚好查询接口被调用。缓存未命中,就去查数据库了,一般写入操作还比查询慢,也就是数据库还未更新,此时查询出来的数据还是之前的,然后被设置到了缓存中,此时数据库更新完成,那么缓存和数据库中的数据就不一致了,这种情况发生的机率还是比较大的。 image.png

2.2、先更新数据库再删除缓存:

这个方案需要担心的就是刚好查询的时候缓存失效了,查到了数据库,但是在写入数据库并删除缓存之后才更新了缓存,那么缓存里面还是脏数据。但是可以通过为缓存设置过期时间来兜底,等它失效了再次查询最终数据是一致的。发生的概率较小。 image.png