zoukankan      html  css  js  c++  java
  • 更新缓存

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

    • 删除(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,这是比较准确的,从库数据有更新,则立即更新缓存

    https://github.com/alibaba/canal

    补充1:缓存穿透

    缓存穿透

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

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

    缓存雪崩

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

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

    缓存击穿

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

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

    补充2:Redis客户端工具Medis

    https://github.com/luin/medis

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

  • 相关阅读:
    数据挂载
    LVS学习与测试——NAT模式
    VirtualBox 网络设置 VirtualBox中客机与主机互通
    [原]两个android程序间的相互调用(apk互调)
    [置顶] Android Service与Activity之间通信的几种方式
    [置顶] android 开发中判断网络是否连接的代码
    [置顶] 判断时间格式是否正确
    [置顶] 输出map信息
    [置顶] checkEmail判断邮箱格式
    [置顶] checkPhone判断手机号格式
  • 原文地址:https://www.cnblogs.com/cjsblog/p/10752245.html
Copyright © 2011-2022 走看看