美文网首页Redis 源码分析与理解
Redis源码及实战分析(二) 如何在生产环境使用缓存

Redis源码及实战分析(二) 如何在生产环境使用缓存

作者: 语落心生 | 来源:发表于2019-07-09 17:01 被阅读0次

    这让我想起了校招回厦门的第二站,当时气血上头应聘5年经验的分布式网络工程师,由于实习期间负责支付模块中用到redis的缓存一致性,所以展开了一场持续1小时的缓存之争

    ### 项目中缓存是如何使用的?
    
    根据缓存一致性,在查询支付数据的时候,为了及时反馈订单,就使用redis保存订单支付和用户在线状态
    
    ### 为什么要用缓存?
    
    用缓存,主要有两个用途:**高性能**、**高并发**。
    
    #### 高性能
    
    假设这么个场景,你有个操作,一个请求过来耗时 600ms查数据库,但是这个结果不会经常变动,或者变了也可以不用立即反馈给用户。应该怎么做?
    
    用缓存,每次先写数据库,再写缓存。每次先查缓存,再查数据库。
    
    #### 高并发
    
    
    但这样有个问题,缓存如果某一时刻为null,那岂不是所有请求都打到数据库?那如果数据库为null,那岂不是所有请求都打到缓存?
    
    enmm缓存是走内存的一部分.....好吧我想不出来
    
    其实要根据具体需求跟业务方协商能不能加机器
    

    但实际上机器也是要维护成本的,所以我在想,如果只有2-3台机器的话,那我们如何应对呢?

    常见的缓存场景有以下几个

    双写不一致

    就像自己之前提到的

    • 读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。
    • 更新的时候,先更新数据库,然后再删除缓存。

    缺点在于:
    一个请求过来,去读缓存,发现缓存空了,去查询数据库,查到了修改前的旧数据,放到了缓存中。随后数据变更的程序完成了数据库的修改。

    方案1:我自己想的是一般是先放入队列,然后起一个短任务轮询最新的请求,如果是写超时没关系,毕竟需要等待最新的写请求,到时间后自动更新。但是读请求超时,就会发生缓存与数据库不一致,所以我的方案是做读写分离,并行化请求,需要频繁访问的接口先读主库,其他接口走从库。

    方案2:一位前辈的建议是更新数据的时候,根据数据的唯一标识,将操作路由之后,发送到一个 jvm 内部队列中。读取数据的时候,如果发现数据不在缓存中,那么将重新读取数据+更新缓存的操作,根据唯一标识路由之后,也发送同一个 jvm 内部队列中。

    缓存雪崩、缓存穿透

    • 缓存雪崩
      对于系统 A,假设每天高峰期每秒 5000 个请求,本来缓存在高峰期可以扛住每秒 4000 个请求,但是缓存机器意外发生了全盘宕机。缓存挂了,此时 1 秒 5000 个请求全部落数据库,数据库报警完就挂了。此时,如果重启数据库,数据库立马又被新的流量给打死了。

    方案1:使用熔断器Hystrix
    对重要的资源 ( 例如 Redis、 MySQL、 Hbase、外部接口 ) 都进行隔离,让每种资源都单独运行在自己的线程池中,即使个别资源出现了问题,对其他服务没有影响。

    方案2:使用redis持久化机制
    一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。

    • 缓存穿透
      当前 key 是一个热点 key( 例如一个热门的娱乐新闻),并发量非常大。
      重建缓存不能在短时间完成,可能是一个复杂计算,例如复杂的 SQL、多次 IO、多个依赖等。


      33.png

    方案1:避免key过期,可以将热点数据设置为永远不过期。但如果重建缓存不能再短时间完成,又会出现数据不一致的情况

    这里需要避免下面三个问题

    • 减少重建缓存的次数
    • 数据尽可能一致
    • 较少的潜在危险

    方案2:基于 redis or zookeeper 实现互斥锁,等待第一个请求构建完缓存之后,再释放锁,进而其它请求才能通过该 key 访问数据。

    缓存并发竞争

    多客户端同时并发写一个 key,可能本来应该先到的数据后到了,导致数据版本错了;或者是多客户端同时获取一个 key,修改值之后再写回去,只要顺序错了,数据就错了。

    方案1:基于时间戳的乐观锁
    你要写入缓存的数据,都是从 mysql 里查出来的,都得写入 mysql 中,写入 mysql 中的时候必须保存一个时间戳,从 mysql 查出来的时候,时间戳也查出来。

    每次要写之前,先判断一下当前这个 value 的时间戳是否比缓存里的 value 的时间戳要新。如果是的话,那么可以写,否则,就不能用旧的数据覆盖新的数据。但缺点在于假设客户A,B,C并发写key,假设有一个审核场景,必须限制A,B,C顺序写。那么就会出现乱序的情况

    方案2:基于redis的setnx的悲观锁

    当要写入key的时候,给每个要访问的定时任务一定的逻辑时间间隔,先进入一个内存队列等待锁,然后排队写入Key

    相关文章

      网友评论

        本文标题:Redis源码及实战分析(二) 如何在生产环境使用缓存

        本文链接:https://www.haomeiwen.com/subject/wrqfkctx.html