redis常见缓存同步更新策略

miloyang
0 评论
/ /
768 阅读
/
2419 字
13 2023-10

缓存用得好,数据错不了。缓存延迟高,早晚得挨刀。

正常企业中使用redis都是当做缓存来用,数据来源一般都是数据库,那数据库的数据和缓存的数据,如何来实现强一致呢?我们来分析下:

redis同步策略

在redis中,缓存有个如下更新策略:

内存淘汰

不用自己维护,利用redis的内存淘汰机制,当内存不足时,自动淘汰部分数据。下次查询的时候再更新缓存。会有不同的配置策略。
这种在一定程度上也保证了一致性,但是不是我们可以控制的。因为我们无法知道哪些数据要淘汰了。
但是没有任何维护成本

超时剔除

我们在给key赋值的时候,顺便赋值一个超时时间,TTL时间,到期后redis会自动删除缓存,然后下次查询时候更新缓存,也在一定程度上保证了一致性。但是他的强弱是根据TTL时间来的,如果还在TTL时间内更新数据了,还是数据不一致。
也没有什么维护成本。

主动更新

我们在编写业务逻辑的时候,在修改数据库同时,也更新缓存,这种肯定在一定程度上做到强一致。
维护成本,是比较高的。
大部分的情况,我们都是采用主动更新。所以我们本文,也着重讲解。

那我们在实际开发中,如何选择呢?
这就需要看业务场景了:

  • 低一致性需求:使用内存淘汰机制,比如我们商场系统中,商品的类型,这个商品类型为衣服,正常情况下是不会变成裤子的。此时,可以选择内存淘汰机制,往里面存就好了。是在不放心,价格超时时间。
  • 高一致性需求:主动更新,并且超时剔除作为兜底方案。比如用户的订单信息,商品的订单信息等等。

主动更新策略

主动更新,一般又分为:

  • 缓存的调用者,在更新数据库的同是更新缓存
  • 缓存与数据库整合一个服务,由服务来维护一致性
  • 其他异步线程来更新缓存

db和缓存同时更新

那又有以下问题考虑:

删除缓存还是更新缓存?

  • 更新缓存:每次更新数据库都更新缓存,无效写操作比较多
  • 删除缓存:更新数据库时,删除缓存让缓存失效,等下次查询时再更新缓存。

所以我们一般情况下,都选择删除缓存

如何保证缓存和数据库的操作同时成功和失败?

必须要保证这两个操作的原子性,不然肯定出现不一致现象。所以根据不同的部署形式来针对不同的方案。

单体系统

单体系统那简单,我们将操作数据库和缓存放入事务中,就保证同时成功和同时失败了。

分布式系统

在分布式架构中,我们的数据库和缓存可能都是不同的服务,那我们就得用到类似于TCC等分布式事务方案了。 至于TCC,不在本文讨论范围之类,可移步分布式事务 TCC

先更新缓存还是先操作数据库?

这里涉及到线程安全的问题,这里得具体看业务的容忍度了,两种皆可以的。

先删除缓存,再操作数据库。

上来就删缓存,然后再操作数据库。至于什么时候更新缓存,得到下一次查询的时候,查询不到了会自动从数据库中读取数据更新缓存。

但是,在多线程非安全的情况下,比如线程1删除缓存,去操作数据库了,但是在还未更新完数据库的时候,线程2来了,查询缓存,没有命中,直接把老数据写入到缓存中。此时线程1更新完数据库了。 那么就会带来缓存和数据库不一致。

这种其实概率还挺高的,因为删很快,但是存db很慢,很容易乘虚而入。

先操作数据库,再删除缓存。

先不管缓存,自己干好业务逻辑后,再删除缓存,下次查询的时候,再更新缓存。

但是,在多线程非安全的情况下,比如线程1查询缓存,此时由于一些原因缓存失效了,就从库中读取数据比如age=20,还未写入缓存。此时线程2来了,他先操作db中的age=30,然后再删除缓存。好,线程1此时写缓存了,把age=20写进去了。

但这种发生的概率较低,因为线程1查缓存再写缓存,一般情况下都是很快的,快到线程2在删除缓存之前就把缓存更新了。

所以,在不都不加锁的情况下,我们尽量采用 先操作数据库,再删除缓存

至于锁,在另外一篇文章中,还在构思...

可以看看这篇:redis-常见问题解决方案 ,里面有关于锁的操作。

人未眠
工作数十年
脚步未曾歇,学习未曾停
乍回首
路程虽丰富,知识未记录
   借此博客,与之共进步