缓存设计

前言

先要明白为什么要使用缓存,这个和缓存的目的/收益有关,缓存可以减少服务端压力、提高响应处理量、缩短响应时间等。如果本身的方案在未来3-5年内足以支撑业务需求,是否需要缓存可以再进行斟酌。现在技术更新很快,可能2-3年后会有更好的通用解决方案。

考虑点

成本和收益

  • 成本
    • 中间需要加一层缓存,需要增加硬件成本以及软件的复杂度
    • 缓存数据和DB中数据由于时差问题,需要考虑数据不一致性问题。能否接受数据的不一致性,或能容忍多长时间的不一致,需要依据数据特性以及业务来决定
  • 收益
    • 减少服务端压力,提高响应处理量,缩短响应时间

原则

  • 离最终的计算结果越近,越有价值。即数据尽可能保证一致
  • 对于需要消耗I/O资源的缓存,要尽可能的减少调用的次数。

缓存颗粒度

  • 缓存整个对象,还是缓存部分所属信息。这个需要根据业务使用场景来识别。
  • 缓存整个对象,内存占用高,可以考虑通用的解决方案。如key不存在时,统一访问DB (select * from table),将整个结果集缓存。
  • 缓存所属信息,内存占用低,但需要根据业务来识别。
    如热点新闻展示,标题的获取是高频的,但内容的获取相对而言是低频的(因为大家只对自己感兴趣的新闻会点击进去查看)。这种情况下,建议缓存标题等关键展示信息。
    此处配图仅做示例说明,不表示真实实现方案
    缓存设计

更新策略

  • 设置过期时间更新
    • 实现简单,短期内可能会存在数据不一致的问题
  • 数据永不过期,由程序主动来更新
    • 增加了实现/维护的复杂度,数据准实时一致性

问题与优化

缓存穿透

问题描述

某个key,从缓存中找不到后会再次请求DB。如果恶意构造大量不存在的key来访问,比单独访问DB的开销会更大,对服务器造成巨大的压力

方案

从缓存本身考虑
* 如果通过key去DB中找不到数据,亦加入缓存,对值进行标记(如null、-1等),后续从缓存中取到该key的信息时,直接返回
* 缓存不过期。即认为缓存是可信、准确的,如果没有取到,即返回。此时需要程序实现来保证,如果数据有变动,能够来主动或定时刷新缓存
其他方面考虑
* 从整个部署的角度来考虑恶意攻击的问题,如黑白名单控制,访问量控制,token拦截、签名、https等

缓存雪崩

问题描述

缓存的数据,到了某个特定的时间点,所有的数据均失效,导致该时间点接收到的所有请求均需要访问DB来刷新缓存

方案

  • 缓存数据的过期时间错开,如在指定的过期时间上增加随机数,如在[-100s,100s]内随机。可接受的时间依据具体业务确定
  • 缓存不过期,使用程序来主动刷新缓存

缓存击穿

问题描述

  • 某个key的缓存过期后,同一时间内有大量的请求均访问该key,由于缓存过期,大量的请求均会访问DB,重建缓存
    缓存设计

方案

  • 重建缓存的过程加锁,保证只有一个人执行,其他人等待
    缓存设计

  • 缓存不过期

tip: 文中提到的DB,是数据源的统称,可以是数据库,亦可以是其他的数据源,如文件、第三方数据提供接口等。

参考资料