多级缓存:架构设计中提升性能最直接的方式

多级缓存

1. 前言

提到缓存,想必每一位软件工程师都不陌生,它是目前架构设计中提高性能最直接的方式。

缓存技术存在于应用场景的方方面面。从网站提高性能的角度分析,缓存可以放在浏览器,可以放在反向代理服务器,还可以放在应用程序进程内,同时可以放在分布式缓存系统中。

从用户请求数据到数据返回,数据经过了浏览器,CDN,Nginx代理缓存,应用服务器,以及数据库各个环节。每个环节都可以运用缓存技术。

多级缓存:架构设计中提升性能最直接的方式

但是在服务器上经常会出现这种情况,一个资源被修改了,但其实际内容根本没发生改变,会因为时间匹配不上而返回了整个实体给客户端(即使客户端缓存里有个一模一样的资源)。为了解决这个问题,HTTP/1.1 推出了。Etag 优先级高与。

2.3 Etag、If-None-Match

都是服务器为每份资源生成的唯一标识,就像一个指纹,资源变化都会导致 ETag 变化,跟最后修改时间没有关系,可以保证每一个资源是唯一的。

在浏览器发起请求,浏览器的请求报文头会包含 字段,其值为上次返回的发送给服务器,服务器接收到次报文后发现 则与被请求资源的唯一标识进行对比。如果相同说明资源没有修改,则响应返 304,浏览器直接从缓存中获取数据信息。如果不同则说明资源被改动过,则响应整个资源内容,返回状态码 200。

3. CDN缓存

CDN:Content Delivery Network,即内容分发网络,它是构建在现有网络基础上的虚拟智能网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、调度及内容分发等功能模块,使用户在请求所需访问的内容时能够就近获取,以此来降低网络拥塞,提高资源对用户的响应速度。

本地存储和浏览器缓存带来的性能提升主要针对的是浏览器端已经缓存了所需的资源,当发生二次请求相同资源时便能够进行快速响应,避免重新发起请求或重新下载全部响应资源。

这些方法对于首次资源请求的性能提升是无能为力的,若想提升首次请求资源的响应速度,除了资源压缩、图片优化等方式,还可借助CDN技术。

多级缓存:架构设计中提升性能最直接的方式

第一步:客户端第一次向Nginx请求数据A;

第二步:当Nginx发现缓存中没有数据A时,会向服务端请求数据A;

第三步:服务端接收到Nginx发来的请求,则返回数据A到Nginx,并且缓存在Nginx;

第四步:Nginx返回数据A给客户端应用;

第五步:客户端第二次向Nginx请求数据A;

第六步:当Nginx发现缓存中存在数据A时,则不会请求服务端;

第七步:Nginx把缓存中的数据A返回给客户端应用。

默认情况下,NGINX尊重Cache-Control源服务器的标头。它不缓存响应Cache-Control设置为Private,No-Cache或No-Store或Set-Cookie在响应头。NGINX只缓存GET和HEAD客户端请求。

如下配置可覆盖这些默认值:

  • proxy_buffering默认为on,若proxy_buffering设置为off,则NGINX不会缓存响应。
  • proxy_ignore_headers可以配置忽略Cache-Control:

5. 进程缓存

通过了客户端,CDN,Nginx代理缓存,我们终于来到了应用服务器。应用服务器上部署着一个个应用,这些应用以进程的方式运行着,那么在进程中的缓存是怎样的呢/p>

进程内缓存又叫托管堆缓存,以 Java 为例,这部分缓存放在 JVM 的托管堆上面,同时会受到托管堆回收算法的影响。

由于其运行在内存中,对数据的响应速度很快,通常我们会把热点数据放在这里。

在进程内缓存没有命中的时候,我们会去搜索进程外的缓存或者分布式缓存。这种缓存的好处是没有序列化和反序列化,是最快的缓存。缺点是缓存的空间不能太大,对垃圾回收器的性能有影响。

目前比较流行的实现有 Ehcache、GuavaCache、Caffeine。这些架构可以很方便的把一些热点数据放到进程内的缓存中。

这里我们需要关注几个缓存的回收策略,具体的实现架构的回收策略会有所不同,但大致的思路都是一致的:

  • FIFO(First In First Out):先进先出算法,最先放入缓存的数据最先被移除。
  • LRU(Least Recently Used):最近最少使用算法,把最久没有使用过的数据移除缓存。
  • LFU(Least Frequently Used):最不常用算法,在一段时间内使用频率最小的数据被移除缓存。

在分布式架构的今天,多应用中如果采用进程内缓存会存在数据一致性的问题。

这里推荐两个方案:

  • 消息队列方案

    应用在修改完自身缓存数据和数据库数据之后,给消息队列发送数据变化通知,其他应用订阅了消息通知,在收到通知的时候修改缓存数据。

  • 定时任务修改方案

    为了避免耦合,降低复杂性,对“实时一致性”不敏感的情况下。每个应用都会启动一个定时任务,定时从数据库拉取最新的数据,更新缓存。

进程内缓存有哪些使用场景呢/p>

  • 场景一:只读数据,可以考虑在进程启动时加载到内存。当然,把数据加载到类似 Redis 这样的进程外缓存服务也能解决这类问题。
  • 场景二:高并发,可以考虑使用进程内缓存,例如:秒杀。

6. 分布式缓存

说完进程内缓存,自然就过度到进程外缓存了。

与进程内缓存不同,进程外缓存在应用运行的进程之外,它拥有更大的缓存容量,并且可以部署到不同的物理节点,通常会用分布式缓存的方式实现。

分布式缓存是与应用分离的缓存服务,最大的特点是,自身是一个独立的应用/服务,与本地应用隔离,多个应用可直接共享一个或者多个缓存应用/服务。

为了提高缓存的可用性,会在原有的缓存节点上加入 Master/Slave 的设计。当缓存数据写入 Master 节点的时候,会同时同步一份到 Slave 节点。

一旦 Master 节点失效,可以通过代理直接切换到 Slave 节点,这时 Slave 节点就变成了 Master 节点,保证缓存的正常工作。

每个缓存节点还会提供缓存过期的机制,并且会把缓存内容定期以快照的方式保存到文件上,方便缓存崩溃之后启动预热加载。

6.1 缓存雪崩

当缓存失效,缓存过期被清除,缓存更新的时候。请求是无法命中缓存的,这个时候请求会直接回源到数据库。

如果上述情况频繁发生或者同时发生的时候,就会造成大面积的请求直接到数据库,造成数据库访问瓶颈。我们称这种情况为缓存雪崩。

从如下两方面来思考解决方案:

缓存方面:

  • 避免缓存同时失效,不同的 key 设置不同的超时时间。
  • 增加互斥锁,对缓存的更新操作进行加锁保护,保证只有一个线程进行缓存更新。缓存一旦失效可以通过缓存快照的方式迅速重建缓存。对缓存节点增加主备机制,当主缓存失效以后切换到备用缓存继续工作。

设计方面,这里给出了几点建议供大家参考:

  • 熔断机制:某个缓存节点不能工作的时候,需要通知缓存代理不要把请求路由到该节点,减少用户等待和请求时长。
  • 限流机制:在接入层和代理层可以做限流,当缓存服务无法支持高并发的时候,前端可以把无法响应的请求放入到队列或者丢弃。
  • 隔离机制:缓存无法提供服务或者正在预热重建的时候,把该请求放入队列中,这样该请求因为被隔离就不会被路由到其他的缓存节点。
  • 如此就不会因为这个节点的问题影响到其他节点。当缓存重建以后,再从队列中取出请求依次处理。

62. 缓存穿透

缓存一般是 Key,Value 方式存在,一个 Key 对应的 Value 不存在时,请求会回源到数据库。

假如对应的 Value 一直不存在,则会频繁的请求数据库,对数据库造成访问压力。如果有人利用这个漏洞攻击,就麻烦了。

解决方法:如果一个 Key 对应的 Value 查询返回为空,我们仍然把这个空结果缓存起来,如果这个值没有变化下次查询就不会请求数据库了。

将所有可能存在的数据哈希到一个足够大的 Bitmap 中,那么不存在的数据会被这个 Bitmap 过滤器拦截掉,避免对数据库的查询压力。

6.3 缓存击穿

在数据请求的时候,某一个缓存刚好失效或者正在写入缓存,同时这个缓存数据可能会在这个时间点被超高并发请求,成为“热点”数据。

这就是缓存击穿问题,这个和缓存雪崩的区别在于,这里是针对某一个缓存,前者是针对多个缓存。

解决方案:导致问题的原因是在同一时间读/写缓存,所以只有保证同一时间只有一个线程写,写完成以后,其他的请求再使用缓存就可以了。

比较常用的做法是使用 mutex(互斥锁)。在缓存失效的时候,不是立即写入缓存,而是先设置一个 mutex(互斥锁)。当缓存被写入完成以后,再放开这个锁让请求进行访问。

itmap 过滤器拦截掉,避免对数据库的查询压力。

6.3 缓存击穿

在数据请求的时候,某一个缓存刚好失效或者正在写入缓存,同时这个缓存数据可能会在这个时间点被超高并发请求,成为“热点”数据。

这就是缓存击穿问题,这个和缓存雪崩的区别在于,这里是针对某一个缓存,前者是针对多个缓存。

解决方案:导致问题的原因是在同一时间读/写缓存,所以只有保证同一时间只有一个线程写,写完成以后,其他的请求再使用缓存就可以了。

比较常用的做法是使用 mutex(互斥锁)。在缓存失效的时候,不是立即写入缓存,而是先设置一个 mutex(互斥锁)。当缓存被写入完成以后,再放开这个锁让请求进行访问。

文章知识点与官方知识档案匹配,可进一步学习相关知识Java技能树使用JDBC操作数据库数据库操作91312 人正在系统学习中

来源:黑马程序员官方

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年9月19日
下一篇 2022年9月19日

相关推荐