缓存技术总结

注:缓存思想很通用,但本文可能会比较偏后端Java开发人员。

概述

定义:

  • 狭义上的缓存,Cache,高速缓冲存储器,一种特殊的存储器子系统,其中复制有频繁使用的数据以利于快速访问。
  • 广义上的缓存,凡是位于速度相差较大的两种硬件/软件之间的,用于协调两者数据传输速度差异的结构,均可称之为 Cache。

缓存可以级联使用,可缓解甚至解决性能问题,无处不在:操作系统磁盘缓存(减少磁盘机械操作)、PC电脑中的内存、CPU中的二级缓存、HTTP协议中的缓存控制、CDN加速、Web服务器缓存、浏览器缓存(减少对网站的访问)、数据库缓存(减少文件系统I/O)、应用程序缓存……

缓存适合的场景:热数据,读多写少,一致性要求不高。

常见缓存

操作系统缓存

  1. 文件系统提供的Disk Cache:操作系统会把经常访问到的文件内容放入到内存当中,由文件系统来管理;
  2. 当应用程序通过文件系统访问磁盘文件的时候,操作系统从Disk Cache当中读取文件内容,加速文件读取速度;
  3. Disk Cache由操作系统来自动管理,一般不用人工干预,但应当保证物理内存充足,以便于操作系统可以使用尽量多的内存充当Disk Cache,加速文件读取速度;
  4. 特殊的应用程序对文件系统Disk Cache有很高的要求,会绕开文件系统Disk Cache,直接访问磁盘分区,自己实现Disk;
  5. Cache策略
    Oracle的raw device(裸设备) – 直接抛弃文件系统
    MySQL的InnoDB: innodb_flush_method = O_DIRECT

数据库缓存

  1. 查询缓存
    Query Cache,以SQL作为key值缓存查询结果集,一旦查询涉及的表记录被修改,缓存就会被自动删除,设置合适的Query Cache会极大提高数据库性能,Query Cache并非越大越好,过大的Qquery Cache会浪费内存。MySQL:query_cache_size= 128M
  2. Data Buffer
    data buffer是数据库数据在内存中的容器,其命中率直接决定数据库的性能,data buffer越大越好,多多益善。MySQL的InnoDB buffer:innodb_buffer_pool_size = 2G,MySQL建议buffer pool开大到服务器物理内存60-80%。

应用缓存

1、对象缓存
由O/R Mapping框架例如Hibernate提供,透明性访问,细颗粒度缓存数据库查询结果,无需业务代码显式编程,是最省事的缓存策略
当软件结构按照O/R Mapping框架的要求进行针对性设计,使用对象缓存将会极大降低Web系统对于数据库的访问请求
良好的设计数据库结构和利用对象缓存,能够提供极高的性能,对象缓存适合OLTP(联机事务处理)应用

页面缓存

作用:针对页面的缓存技术不但可以减轻数据库服务器压力,还可以减轻应用服务器压力,好的页面缓存可以极大提高页面渲染速度,页面缓存的难点在于如何清理过期的缓存。
分类
动态页面静态化
利用模板技术将访问过一次的动态页面生成静态html,同时修改页面链接,下一次请求直接访问静态链接页面
动态页面静态化技术的广泛应用于互联网CMS/新闻类Web应用,但也有BBS应用使用该技术,例如Discuz!
无法进行权限验证,无法显示个性化信息
可以使用AJAX请求弥补动态页面静态化的某些缺点
II、Servlet缓存
针对URL访问返回的页面结果进行缓存,适用于粗粒度的页面缓存,例如新闻发布
可以进行权限的检查
OScache提供简单的Servlet缓存(通过web.xml中的配置)
也可以自己编程实现Servlet缓存
III、页面内部缓存
针对动态页面的局部片断内容进行缓存,适用于一些个性化但不经常更新的页面(例如博客)
OSCache提供了简单的页面缓存
可以自行扩展JSP Tag实现页面局部缓存

服务器缓存

  • 反向代理服务器缓存,如nginx;
  • 静态站点Web服务器缓存,如squid/nginx;
  • 静态资源内容分发服务器缓存,如CDN;
  • servlet服务器缓存,如Tomcat;

浏览器缓存

缓存分类

  1. 本地缓存和远程缓存

本地缓存

也叫进程缓存,实现技术:

  • guava cache
  • ehcache
  • Caffeine

参考本地缓存及Guava Cache&Caffeine使用

远程缓存

也叫分布式缓存,实现技术:

  • redis
  • memcache

缓存问题

缓存颠簸

也叫缓存抖动,一般是由于缓存节点故障导致,一致性Hash算法。

缓存雪崩

产生原因:高并发请求,缓存在同一时间大量失效,查db进而打垮db。
解决方案:

  1. 过期时间加随机值;
  2. 如果Redis是集群部署,将热点数据均匀分布在不同的Redis库中也能避免全部失效。
  3. 设置热点数据永不过期

缓存击穿

产生原因:超级热点Key,扛着大量的请求,当Key在失效的瞬间,大并发直接落到数据库上,发生Key值的缓存击穿。和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。
解决方案:

  1. 设置热点数据永不过期
  2. 加上互斥锁:在根据key获得的value值为空时,先锁上,再从数据库加载,加载完毕,释放锁。若其他线程发现获取锁失败,则睡眠50ms后重试。
  3. 布隆过滤器:BloomFilter能够迅速判断一个元素是否在一个集合中。

缓存穿透

产生原因:查询缓存中必定不存在的数据,缓存查询cache_miss,导致查询走到db层,流量大可能导致db挂掉。
解决方案:

  1. 在接口层增加校验:用户鉴权,参数做校验,不合法的校验直接 return
  2. 布隆过滤器(Bloom Filter),利用高效的数据结构和算法快速判断出这个 Key 是否在数据库中存在,不存在则return
  3. 后台定时任务job,读数据库,写更新到缓存。这种方案比较容易理解,但会增加系统复杂度。比较适合那些key相对固定、cache粒度较大的业务,key比较分散的则不太适合,实现也比较复杂

总结

缓存穿透和缓存击穿,很相似,都是缓存未命中。击穿是刚好key失效,穿透是key不存在。

上面三种问题的通用解决方案:

  1. 直接缓存NULL值
  2. 限流、降级、熔断
  3. 缓存预热
  4. 分级缓存,多级缓存
  5. 缓存永远不过期

其他

Buffer & Cache

非常容易混用的概念。
Buffer,缓冲,缓和冲击
Cache,缓存/快取,加快取用的速度
硬盘的读写缓冲/缓存名称是不一样的,write-buffer和read-cache。
一般都是读写混用,CPU里的L2和L3 Cache也都是读写兼用。当然也有读buffer
拿cache做buffer用呢?只要能控制cache淘汰逻辑就没有任何问题。
拿buffer做cache用呢?在很特殊的情况下,能确定访问顺序的时候,也是可以的,但是比较局限

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章