欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

更新缓存

程序员文章站 2023-10-17 08:33:21
更新缓存的时候涉及两个问题: 删除(del)还是 修改(set)? 先操作数据库,还是 先操作缓存? 组合起来就有四种情况: 第一种情况:先删除缓存,后更新数据库 如果删除缓存失败,则后面的操作都不会执行,没问题; 如果删除缓存成功,更新数据库失败,则缓存与数据库不一致,但这种不一致会马上被修正, ......

更新缓存的时候涉及两个问题:

  • 删除(del)还是 修改(set)?
  • 先操作数据库,还是 先操作缓存?

组合起来就有四种情况:

第一种情况:先删除缓存,后更新数据库

如果删除缓存失败,则后面的操作都不会执行,没问题;

如果删除缓存成功,更新数据库失败,则缓存与数据库不一致,但这种不一致会马上被修正,因而不影响,因为下一次请求缓存的时候发现缓存中没有,会从数据库重新加载;但是,又有一个问题出现了,在旧的缓存被删除后,新的缓存未写入之前,这段时间内如果有读操作,那么旧的值会被重新加载到缓存,这就相当于没更新缓存;

第二种情况:先更新缓存,后更新数据库

同样,如果更新缓存成功,更新数据库是吧,则出现缓存与数据库不一致,数据不一致就是问题

第三种情况:先更新数据库,后删除缓存

如果更新数据库成功,删除缓存失败,则出现缓存与数据库不一致,数据不一致就是问题

第四种情况:先更新数据库,后更新缓存

跟第三种情况一样

 

虽然,看上去好像都有问题,但是,任何脱离实际业务的设计都是耍流氓

既然我们把redis当缓存,那么所有数据都要以数据库为准,像上面第二种情况(缓存中有的数据在数据库中没有)是不能容忍的,而对于第一种情况,可以采取双删的策略(删除缓存 --> 更新数据库 --> 再删除缓存),后面两种情况,可以用定时任务进行补偿,有些场景下我们是可以接受不一致的情况的。

不过,话又说回来,直接删除缓存当然是最简单的,它相当于延迟加载(第一次使用的时候发现没有才会去从数据库加载),这样可能导致第一次请求会比较慢;而采用修改缓存的方式,相当于预先加载。

在实际使用的时候,可以采用这两种方式:

  1. 先删除缓存,再更新数据库,最后再删一次
  2. 先更新数据库,然后向mq发一条消息,由专门的缓存服务去更新数据

 

上面说的是只有一个数据库实例的情况,而实际生产过程中肯定是一主多从的

按照写主读从,缓存加载数据的时候应该从从库中读,而本来主从同步就有延迟,于是读从库很有可能读到的是旧数据

为了解决这种问题,可以考虑以下几种方案:

第一种:强制缓存读主数据库

这样一来,就不必考虑主从同步的问题了,可行(ps:跟微信公众号开发的时候获取token一样)

第二种:选择性地读主数据库

之所以强制读主库,是因为再主从同步完成之前从库中的数据还是旧的,当主从同步完成后再读从库就没什么问题了,那么如果在主从同步的这段时间内如果没有请求读这个key就没有问题,如果这段时间内有请求读取这个key,那么在同步完成后要删除这个key

如何判断在主从同步这段时间内有没有请求读取这个key呢?

在更新数据库的时候,往缓存中设置一个key,格式是:缓存key+业务数据id,其生存时间是主从延时时间

比如,假设主从同步延时是3秒,而有业务缓存key是hash类型的,更新的这条数据的id是213,那么在更新数据库后要立即设置  set user_213_kv  1  3

在读的时候,首先判断缓存中有没有这样一个key,如果有则从主库中重新加载数据到缓存,没有,则直接从从库中加载数据到缓存

第三种:订阅从库的binlog

可以通过工具(比如,canal)订阅从库的binlog,这是比较准确的,从库数据有更新,则立即更新缓存

 

补充1:缓存穿透

缓存穿透

缓存穿透,指的是查询一个数据库中不存在的数据。这样的话,每次都会查询数据库,相当于缓存就没有用了。

针对这种情况,可以缓存空值,并设置一个较短的生存时间,比如60秒。

 

缓存雪崩

缓存雪崩,指的是大量缓存在一段时间内集体失效。这样的话,短时间内大量请求会直接打到数据库。

针对这种情况,可以在缓存的生存时间后面再加上一个随机数,这样的话就不至于同一时刻集体过期。实际上,因为大量缓存失效意味着这些缓存在同一时刻被设置的,而这种情况不多见。

 

缓存击穿

缓存击穿,指的是单个缓存在被高并发访问时失效了导致请求全部打到数据库。

针对这种情况,在加载缓存的时候要加分布式锁。

 

补充2:redis客户端工具medis

git clone https://github.com/luin/medis.git
cd medis
npm install
npm run build
npm start

更新缓存

更新缓存