缓存应用设计中的经验总结、注意事项

关注公众号 架构师之路很久,沈剑分享的很多干货,收益良多。结合工作需要整理一下缓存应用中需要注意的问题。

缓存无处不在,跨进程的缓存:浏览器本地缓存–>cdn–>webServer(Nginx)–>appServer(tomcat,jetty,WAS)–>缓存服务器–>DB。

缓存的分类介绍

进程内缓存,一级缓存,二级缓存。

缓存分类

本地缓存

  • 特点
    优点:store in jvm;本地访问,快速访问,省去网络开销;各节点独立存储,适合小量数据,只读数据。
    缺点:存储量有限,一份数据复制多份,每个jvm一份,浪费内存,多节点间数据更新同步复杂,影响服务无状态化,开发复杂度提高。
  • 常见实现方式
    HashMap
    ConcurrentHashMap,
    Ehcache
    • 使用场景
      少量高频只读数据
    • 缓存更新方式
      应用启动时,初始化缓存,缓存预热。
      读缓存,过期则 单线程从DB获取数据,存入本地缓存。
      源数据更新,直接删除缓存。 缓存只提供读服务,不做缓存的更新修改。
      多节点部署时,源数据更新,发消息到MQ,各个服务节点消费消息,删除本地缓存。

缓存服务-分布式缓存服务

  • Redis
  • MemCache
  • Apache Ignite

缓存常见误用

现象: 缓存当数据管道使用,多个应用进程读写同一个缓存服务

  • 使用不当原因
    - 应用间耦合严重,缓存偏向于数据存储层,存储直接暴漏给外部系统,架构分层上不合理。
    - 服务器间Rpc通信,暴漏接口,针对接口建立协议,而不是针对实现。
    - 数据权限归属不清
    - 系统读写速度不同,互相影响,资源使用存在争抢问题。
  • 解决方案
    系统间通过服务接口约定交互协议。通过MQ解耦,缓存只是接口实现层面的事情。

现象:缓存雪崩

  • 原因:缓存服务整体挂掉,导致流量压垮源数据库,导致源数据库启动不了。
  • 解决办法:
    容量规划、缓存水平切分,缓存局部挂掉,不影响整体。
    缓存失效策略
    限流方案

现象:调用方缓存数据

  • 原因:开发人员原因
  • 解决办法:
    服务方缓存,作为服务的实现方式,对调用方隐藏。服务调用方只通过接口访问数据,客户端与缓存解耦合。

现象:缓存穿透

  • 原因:访问大量不存在的数据,导致回源流量过大,给DB带来很大压力。

  • 解决办法 :
    缓存空对象。如果缓存未命中,而数据库中也没有这个对象,则可以缓存一个空对象到缓存。如果使用Redis,这种key需设置一个较短的时间,以防内存浪费。
    缓存预测,预测key是否存在,如果缓存的量不大可以使用hash来判断,如果量大可以使用布隆过滤器来做判断.

  • 缓存更新策略
    单线程独立写数据到缓存服务

缓存、源系统读写一致性问题

  • WriteThrough
  • ReadThrough
    • 什么是“Cache Aside Pattern”?
    • 读过程
      先读cache,再读db
      如果,cache hit,则直接返回数据
      如果,cache miss,则访问db,并将数据set回缓存
    • 写过程
      第一步要操作数据库,第二步操作缓存
      缓存,采用delete淘汰,而不是set更新
      写过程–** 争议的地方**
      考虑DB,缓存服务都是集群的情况:
      1、先写DB,后同步缓存
      正常情况:
      db主–>db从同步完成–>缓存读新值,缓存存放最新的值。
      异常情况:
      db主写–>缓存服务读db从(旧值)–>db从同步新值。缓存存放旧值直到过期。
      解决办法:
      业务需求不要求实时一致,可忽略不处理。
      技术上解决写时的不一致:
    • 第一种办法:
      1、写数据时记录key值到分布式cache.预估数据同步时间段,设置key缓存有效期和同步时间一致。
      2、读数据时,先从分布式cache查询key是否存在,若存在,则数据正在同步中,读节点为脏数据,从主节点读取,不存在,数据没有修改,从节点读取。
      当然这种方案,依赖cache的高可用,只是缩小数据不一致的时间段,同时,
    • 另一种办法
      1、先写DB,db节点数据同步的同时,异步发消息到MQ
      2、缓存服务消费MQ内容,淘汰数据,从主DB同步最新数据。
      此种办法,依赖MQ的高可用,消息有序。

总之,不管那种方案,基础服务,消息中间件的稳定性、可用性都是必须要有保障的,越基础的越重要。写服务数据主从同步期间,会出现短暂不一致,业务上是否可接受缓存数据的暂时不准确是首先要考虑的。

原文地址:https://www.cnblogs.com/coding-now/p/14660599.html