万金油之缓存

在互联网产品中,缓存无处不在。当一个网站前期请求量少,服务响应很快,当随着请求增加响应越来越慢。这时需要对请求的入口合并、业务调整、底层代码重构、数据库SQL与表等等优化后提高响应最终提升整体的性能,也可以引入缓存以提高吞吐量。但是引入缓存也带了维护的问题,比如脏数据的问题、缓存数据更新的策越等等。

缓存的存在的场景

例如

  • 浏览器缓存包括静态资源js、css、图片、video等数据
  • 后端请求数据的缓存
  • 对象缓存
  • 网络数据的读取写入缓存
  • APP内数据的缓存
  • CPU一级、二级缓存

等等非常的多。

命中率

是衡量缓存的一个重要的指标

命中率 = 从缓存Get数据的次数 / 请求缓存总次数(包括load db、null等)

淘汰策略

  • LRU:

LRU(least recently used)

最近最少使用策略,每次请求动态刷新数据最后一次的使用时间,当触发淘汰时,清除最久使用的元素。

适合热点数据

不适合存储类似系统配置的数据,不是热点数据,但是却不能淘汰。

  • FIFO:

FIFO(first in first out)

当达到触发清理时机时(空间不足或者达到元素上限)最先进入的元素会被优先处理掉,依赖元素的创建时间,

适合使用实效性的场景,例如日志、指标采集等数据。 保护最新的数据

但是如果先进入的数据为热点数据或者系统常量配置。不适合

  • LFU:

LFU(less frequently used)

最近很少使用策略,无论是否过期,根据元素的被使用次数判断,清除使用次数较少的元素释放空间。

策略算法主要比较元素的hitCount(命中次数)。在保证高频数据有效性场景下,可选择这类策略。

  • W-TinyLFU(Window Tiny Least Frequently Used)

算法:当一个数据进来的时候,会进行筛选比较,进入W-LRU窗口队列,以此应对流量突增,经过淘汰后进入过滤器,通过访问访问频率判决是否进入缓存。如果一个数据最近被访问的次数很低,那么被认为在未来被访问的概率也是最低的,当规定空间用尽的时候,会优先淘汰最近访问次数很低的数据;

优点:使用Count-Min Sketch算法存储访问频率,极大的节省空间;定期衰减操作,应对访问模式变化;并且使用window-lru机制能够尽可能避免缓存污染的发生,在过滤器内部会进行筛选处理,避免低频数据置换高频数据。

缓存的容量

每台机器物理上都是有上限的,所以缓存的容量不能无限的增加,当达到物理的上限后,会导致物理机运行缓慢。

存在哪里

  • 内存: 直接开辟内存进行数据存储,速度开,IO延迟低,掉电数据丢失。适合数据允许丢失,并且有一定的回源策略
  • 磁盘: 采用文件方式存储。例如redis中的save动作、MySQL数据库。数据都会保存到磁盘,并且选择性的加到到内存,IO延迟高。成本低。数据不易丢失(全备、增量、快照等备份),

现有的缓存都是2者结合,优先内存,定期保存到磁盘。出现故障时从磁盘中恢复, 有时恢复数据会慢, 可以摘掉一部分流量

本地与分布式

  • Guava Caffeine

本地缓存代表的产品Guava、EhCache、Caffeine(Spring 5优先推荐使用)

  • memcahce redis

分布式缓存代表的产品memcache、redis其他NoSQL内存类产品

  • 一致性算法

主要解约部分增加节点,不会对全部数据进行hash取模计算。

一致性Hash算法原理与实现

缓存击穿、穿透、雪崩

  • 穿透: 缓存+DB都不存在,并且请求特点是,短时间、高密度的。
  • 击穿: 大量请求并发查询同一条数据(同一个key)
  • 雪崩: 在一个时刻,大量的缓存(不同的key)到期,将请求全部请求到DB

热点数据

参考