如何使用缓存优化系统性能?

缓存是我们提高系统性能的一项必不可少的技术,无论是前端、还是后端,都应用到了缓存
技术。前端使用缓存,可以降低多次请求服务的压力;后端使用缓存,可以降低数据库操作
的压力,提升读取数据的性能。
今天我们将从前端到服务端,系统了解下各个层级的缓存实现,并分别了解下各类缓存的优
缺点以及应用场景。
前端缓存技术
如果你是一位 Java 开发工程师,你可能会想,我们有必要去了解前端的技术吗? 不想当将军的士兵不是好士兵,作为一个技术人员,不想做架构师的开发不是好开发。作为
架构工程师的话,我们就很有必要去了解前端的知识点了,这样有助于我们设计和优化系
统。前端做缓存,可以缓解服务端的压力,减少带宽的占用,同时也可以提升前端的查询性
能。
1. 本地缓存
平时使用拦截器(例如 Fiddler)或浏览器 Debug 时,我们经常会发现一些接口返回 304
状态码 + Not Modified 字符串。
如果我们对前端缓存技术不了解,就很容易对此感到困惑。浏览器常用的一种缓存就是这种
基于 304 响应状态实现的本地缓存了,通常这种缓存被称为协商缓存。
协商缓存,顾名思义就是与服务端协商之后,通过协商结果来判断是否使用本地缓存。
一般协商缓存可以基于请求头部中的 If-Modified-Since 字段与返回头部中的 Last
Modified 字段实现,也可以基于请求头部中的 If-None-Match 字段与返回头部中的 ETag
字段来实现。
两种方式的实现原理是一样的,前者是基于时间实现的,后者是基于一个唯一标识实现的,
相对来说后者可以更加准确地判断文件内容是否被修改,避免由于时间篡改导致的不可靠问
题。下面我们再来了解下整个缓存的实现流程:
当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response
头部加上 ETag 唯一标识,这个唯一标识的值是根据当前请求的资源生成的;
当浏览器再次请求访问服务器中的该资源时,会在 Request 头部加上 If-None-Match
字段,该字段的值就是 Response 头部加上 ETag 唯一标识; 服务器再次收到请求后,会根据请求中的 If-None-Match 值与当前请求的资源生成的唯
一标识进行比较,如果值相等,则返回 304 Not Modified,如果不相等,则在
Response 头部加上新的 ETag 唯一标识,并返回资源;
如果浏览器收到 304 的请求响应状态码,则会从本地缓存中加载资源,否则更新资源。
本地缓存中除了这种协商缓存,还有一种就是强缓存的实现。
强缓存指的是只要判断缓存没有过期,则直接使用浏览器的本地缓存。 如下图中,返回的是
200 状态码,但在 size 项中标识的是 memory cache。

强缓存是利用 Expires 或者 Cache-Control 这两个 HTTP Response Header 实现的,它
们都用来表示资源在客户端缓存的有效期。
Expires 是一个绝对时间,而 Cache-Control 是一个相对时间,即一个过期时间大小,与
协商缓存一样,基于 Expires 实现的强缓存也会因为时间问题导致缓存管理出现问题。我建
议使用 Cache-Control 来实现强缓存。具体的实现流程如下:
当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response
头部加上 Cache-Control,Cache-Control 中设置了过期时间大小;
浏览器再次请求访问服务器中的该资源时,会先通过请求资源的时间与 Cache-Control
中设置的过期时间大小,来计算出该资源是否过期,如果没有,则使用该缓存,否则请求
服务器;
服务器再次收到请求后,会再次更新 Response 头部的 Cache-Control。

2. 网关缓存
除了以上本地缓存,我们还可以在网关中设置缓存,也就是我们熟悉的 CDN。
CDN 缓存是通过不同地点的缓存节点缓存资源副本,当用户访问相应的资源时,会调用最
近的 CDN 节点返回请求资源,这种方式常用于视频资源的缓存。
服务层缓存技术
前端缓存一般用于缓存一些不常修改的常量数据或一些资源文件,大部分接口请求的数据都
缓存在了服务端,方便统一管理缓存数据。
服务端缓存的初衷是为了提升系统性能。例如,数据库由于并发查询压力过大,可以使用缓
存减轻数据库压力;在后台管理中的一些报表计算类数据,每次请求都需要大量计算,消耗
系统 CPU 资源,我们可以使用缓存来保存计算结果。
服务端的缓存也分为进程缓存和分布式缓存,在 Java 中进程缓存就是 JVM 实现的缓存,
常见的有我们经常使用的容器类,ArrayList、ConcurrentHashMap 等,分布式缓存则是
基于 Redis 实现的缓存。

1. 进程缓存
对于进程缓存,虽然数据的存取会更加高效,但 JVM 的堆内存数量是有限的,且在分布式
环境下很难同步各个服务间的缓存更新,所以 我们一般缓存一些数据量不大、更新频率较低
的数据。 常见的实现方式如下:

Version:0.9 StartHTML:0000000105 EndHTML:0000002093 StartFragment:0000000141 EndFragment:0000002053

1 // 静态常量
2 public final staticS String url = "https://time.geekbang.org";
3 //list 容器
4 public static List<String> cacheList = new Vector<String>();
5 //map 容器
6 private static final Map<String, Object> cacheMap= new ConcurrentHashMap<String, Object

除了 Java 自带的容器可以实现进程缓存,我们还可以基于 Google 实现的一套内存缓存组
件 Guava Cache 来实现。 Guava Cache 适用于高并发的多线程缓存,它和 ConcurrentHashMap 一样,都是基于
分段锁实现的并发缓存。
Guava Cache 同时也实现了数据淘汰机制,当我们设置了缓存的最大值后,当存储的数据
超过了最大值时,它就会使用 LRU 算法淘汰数据。我们可以通过以下代码了解下 Guava
Cache 的实现:
1 public class GuavaCacheDemo {
2 public static void main(String[] args) {
3 Cache<String,String> cache = CacheBuilder.newBuilder()
4 .maximumSize(2)
5 .build();
6 cache.put("key1","value1");
7 cache.put("key2","value2");
8 cache.put("key3","value3");
9 System.out.println(" 第一个值: " + cache.getIfPresent("key1"));
10 System.out.println(" 第二个值: " + cache.getIfPresent("key2"));
11 System.out.println(" 第三个值: " + cache.getIfPresent("key3"));
12 }
13 }

那如果我们的数据量比较大,且数据更新频繁,又是在分布式部署的情况下,想要使用
JVM 堆内存作为缓存,这时我们又该如何去实现呢?
Ehcache 是一个不错的选择,Ehcache 经常在 Hibernate 中出现,主要用来缓存查询数据
结果。Ehcache 是 Apache 开源的一套缓存管理类库,是基于 JVM 堆内存实现的缓存,
同时具备多种缓存失效策略,支持磁盘持久化以及分布式缓存机制。
2. 分布式缓存 由于高并发对数据一致性的要求比较严格,我一般不建议使用 Ehcache 缓存有一致性要求
的数据。对于分布式缓存,我们建议使用 Redis 来实现,Redis 相当于一个内存数据库,
由于是纯内存操作,又是基于单线程串行实现,查询性能极高,读速度超过了 10W 次 /
秒。
Redis 除了高性能的特点之外,还支持不同类型的数据结构,常见的有 string、list、set、
hash 等,还支持数据淘汰策略、数据持久化以及事务等。
两种缓存讲完了,接下来我们看看其中可能出现的问题。
数据库与缓存数据一致性问题
在查询缓存数据时,我们会先读取缓存,如果缓存中没有该数据,则会去数据库中查询,之
后再放入到缓存中。
当我们的数据被缓存之后,一旦数据被修改(修改时也是删除缓存中的数据)或删除,我们
就需要同时操作缓存和数据库。这时,就会存在一个数据不一致的问题。
例如,在并发情况下,当 A 操作使得数据发生删除变更,那么该操作会先删除缓存中的数
据,之后再去删除数据库中的数据,此时若是还没有删除成功,另外一个请求查询操作 B
进来了,发现缓存中已经没有了数据,则会去数据库中查询,此时发现有数据,B 操作获取
之后又将数据存放在了缓存中,随后数据库的数据又被删除了。此时就出现了数据不一致的
情况。
那如果先删除数据库,再删除缓存呢?
我们可以试一试。在并发情况下,当 A 操作使得数据发生删除变更,那么该操作会先删除
了数据库的操作,接下来删除缓存,失败了,那么缓存中的数据没有被删除,而数据库的数
据已经被删除了,同样会存在数据不一致的问题。
所以,我们还是需要先做缓存删除操作,再去完成数据库操作。那我们又该如何避免高并发
下,数据更新删除操作所带来的数据不一致的问题呢?
通常的解决方案是,如果我们需要使用一个线程安全队列来缓存更新或删除的数据,当 A
操作变更数据时,会先删除一个缓存数据,此时通过线程安全的方式将缓存数据放入到队列
中,并通过一个线程进行数据库的数据删除操作。
当有另一个查询请求 B 进来时,如果发现缓存中没有该值,则会先去队列中查看该数据是
否正在被更新或删除,如果队列中有该数据,则阻塞等待,直到 A 操作数据库成功之后,
唤醒该阻塞线程,再去数据库中查询该数据。
但其实这种实现也存在很多缺陷,例如,可能存在读请求被长时间阻塞,高并发时低吞吐量
等问题。所以 我们在考虑缓存时,如果数据更新比较频繁且对数据有一定的一致性要求,我
通常不建议使用缓存。
缓存穿透、缓存击穿、缓存雪崩
对于分布式缓存实现大数据的存储,除了数据不一致的问题以外,还有缓存穿透、缓存击
穿、缓存雪崩等问题,我们平时实现缓存代码时,应该充分、全面地考虑这些问题。
缓存穿透是指大量查询没有命中缓存,直接去到数据库中查询,如果查询量比较大,会导致
数据库的查询流量大,对数据库造成压力。
通常有两种解决方案,一种是将第一次查询的空值缓存起来,同时设置一个比较短的过期时
间。但这种解决方案存在一个安全漏洞,就是当黑客利用大量没有缓存的 key 攻击系统
时,缓存的内存会被占满溢出。
另一种则是使用布隆过滤算法(BloomFilter),该算法可以用于检查一个元素是否存在,
返回结果有两种:可能存在或一定不存在。这种情况很适合用来解决故意攻击系统的缓存穿
透问题,在最初缓存数据时也将 key 值缓存在布隆过滤器的 BitArray 中,当有 key 值查询
时,对于一定不存在的 key 值,我们可以直接返回空值,对于可能存在的 key 值,我们会
去缓存中查询,如果没有值,再去数据库中查询。
BloomFilter 的实现原理与 Redis 中的 BitMap 类似,首先初始化一个 m 长度的数组,并
且每个 bit 初始化值都是 0,当插入一个元素时,会使用 n 个 hash 函数来计算出 n 个不
同的值,分别代表所在数组的位置,然后再将这些位置的值设置为 1。
假设我们插入两个 key 值分别为 20,28 的元素,通过两次哈希函数取模后的值分别为 4,9
以及 14,19,因此 4,9 以及 14,19 都被设置为 1。

 

那为什么说 BloomFilter 返回的结果是可能存在和一定不存在呢?
假设我们查找一个元素 25,通过 n 次哈希函数取模后的值为 1,9,14。此时在 BitArray 中
肯定是不存在的。而当我们查找一个元素 21 的时候,n 次哈希函数取模后的值为 9,14,此
时会返回可能存在的结果,但实际上是不存在的。
BloomFilter 不允许删除任何元素的,为什么?假设以上 20,25,28 三个元素都存在于
BitArray 中,取模的位置值分别为 4,9、1,9,14 以及 14,19,如果我们要删除元素 25,此
时需要将 1,9,14 的位置都置回 0,这样就影响 20,28 元素了。
因此,BloomFilter 是不允许删除任何元素的,这样会导致已经删除的元素依然返回可能存
在的结果,也会影响 BloomFilter 判断的准确率,解决的方法则是重建一个 BitArray。
那什么缓存击穿呢?在高并发情况下,同时查询一个 key 时,key 值由于某种原因突然失
效(设置过期时间或缓存服务宕机),就会导致同一时间,这些请求都去查询数据库了。这
种情况经常出现在查询热点数据的场景中。通常我们会在查询数据库时,使用排斥锁来实现
有序地请求数据库,减少数据库的并发压力。
缓存雪崩则与缓存击穿差不多,区别就是失效缓存的规模。雪崩一般是指发生大规模的缓存
失效情况,例如,缓存的过期时间同一时间过期了,缓存服务宕机了。对于大量缓存的过期
时间同一时间过期的问题,我们可以采用分散过期时间来解决;而针对缓存服务宕机的情
况,我们可以采用分布式集群来实现缓存服务。
总结
从前端到后端,对于一些不常变化的数据,我们都可以将其缓存起来,这样既可以提高查询
效率,又可以降低请求后端的压力。对于前端来说,一些静态资源文件都是会被缓存在浏览
器端,除了静态资源文件,我们还可以缓存一些常量数据,例如商品信息。 服务端的缓存,包括了 JVM 的堆内存作为缓存以及 Redis 实现的分布式缓存。如果是一些
不常修改的数据,数据量小,且对缓存数据没有严格的一致性要求,我们就可以使用堆内存
缓存数据,这样既实现简单,查询也非常高效。如果数据量比较大,且是经常被修改的数
据,或对缓存数据有严格的一致性要求,我们就可以使用分布式缓存来存储。
在使用后端缓存时,我们应该注意数据库和缓存数据的修改导致的数据不一致问题,如果对
缓存与数据库数据有非常严格的一致性要求,我就不建议使用缓存了。
同时,我们应该针对大量请求缓存的接口做好预防工作,防止查询缓存的接口出现缓存穿
透、缓存击穿和缓存雪崩等问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/1619123.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

关键词优化系统|智能优化「白狐公羊seo」

1.SEO的设置 。要进行SEO网站优化的前提&#xff0c;先设置TDK&#xff0c;三个重要的参数:title、keywords、description。因为TDK是一个网站优化的核心要素和重要标志。后期的优化都是围绕着TDK来进行的。设置TDK是对 网站优化排名 提升的其中一个方法。 2. 内容标题优化…

windows10 优化系统性能

以下仅供参考&#xff1a; 1、设置中 2、 设置中》搜索【启动应用】&#xff0c;关闭启动机器自动启动的应用 或者在任务管理器中 3、回收站 右键【属性】》一般设置为10G到15G之间 4、电源计划中设置高性能 考虑到个人健康 power shell命令中&#xff1a; 然后【更改计划】…

大规模C++编译性能优化系统OMAX介绍

导读&#xff1a;本文探索&研究了大规模C/C服务编译性能优化相关技术&#xff0c;优化服务性能&#xff0c;降低机器成本&#xff0c;同时为了支持规模推广应用&#xff0c;降低业务线接入成本&#xff0c;保障优化效果&#xff0c;进行面向云上微服务&#xff0c;开展平台…

系统优化方案

亲爱的用户&#xff0c;当您觉得计算机速度过慢&#xff0c;或者您想清理系统垃圾的话&#xff0c;您只需按照以下步骤操作即可哦(&#xff3e;&#xff35;&#xff3e;)ノ~&#xff39;&#xff2f; 一、手动清除系统盘的缓存文件&#xff1a; 打开桌面【此电脑】图标&#x…

如何进行系统性能优化

前言 不战而屈人之兵&#xff0c;善之善者也 性能优化的第一原则是&#xff0c;通过测试&#xff0c;日志&#xff0c;profiling 分析出哪有问题&#xff0c;然后有的放失。 性能优化时持久战&#xff0c;在深入理解业务后&#xff0c;结合系统响应&#xff0c;系统吞吐&#…

win10计算机系统优化设置,win10系统优化系统的详细办法

作为大多数用户都使用的win10系统&#xff0c;如果发生对win10系统优化系统进行设置情况&#xff0c;会让人束手无策&#xff0c;那么win10系统优化系统是怎么设置的呢&#xff1f;如果有朋友想对win10系统优化系统进行设置的话&#xff0c;按照1、右键点击“此电脑”&#xff…

系统优化的方法有哪些

对于很多人来说&#xff0c;系统优化可能只是个概念&#xff0c;真正想要操作的时候却又无从下手&#xff0c;然后一个不小心把系统搞崩溃了又凉凉。下面就和大家简单聊聊系统优化应该从哪几个方面去操作吧。 更多系统教程尽在小白系统重装官网 系统&#xff1a;win10专业版 …

八 在IDEA中设置省电模式

如下图所示&#xff0c;IntelliJ IDEA 有一种叫做 省电模式 的状态&#xff0c;开启这种模式之后IntelliJ IDEA 会关掉代码检查和代码提示等功能。所以一般也可认为这是一种 阅读模式&#xff0c;如果你在开发过程中遇到突然代码文件不能进行检查和提示&#xff0c;可以来看看这…

嵌入式linux更省电,- 基于嵌入式Linux的智能手机省电设计

用户程序API&#xff0c; 用户程序( 应用软件) 分为三类&#xff1a; (1)可感知电源管理的应用软件; (2)可感知电源管理的“包装器”中的传统应用软件; (3)不带电源管理的传统应用软件。 可感知电源管理的应用软件能够充分利用来自策略管理器的API&#xff0c; 从而建立各自的基…

一级节能和三级节能有什么区别

等级不同&#xff1a;一级节能产品能源效率达国际先进水平&#xff0c;三级的为市场平均水平。能耗量不同&#xff1a;一级和三级节能产品全年耗电量分别为634度781度。省电程度不同&#xff1a;同规格产品在同样条件下&#xff0c;一级节能产品比三级的节能15%左右。 以变频空…

计算机系统节能减排,节能减排,我们怎么做?

原标题&#xff1a;节能减排&#xff0c;我们怎么做&#xff1f; 节能减排 节俭但不算计&#xff0c;环保但不做作 节能环保已被越来越多的人接受 形成了一种特定的文化 保护环境并没有想象中的那么难 只要我们从小事做起 就会让地球母亲焕然一新 空用完拔插头&#xff0c;省电…

android屏幕省电,AMOLED屏幕省电吗 AMOLED屏幕耗电测评测

毫无疑问屏幕是最能够直接影响用户体验的部件之一&#xff0c;而关于AMOLED屏幕好还是LCD屏幕好的争论也从未停止。与LCD屏幕相比&#xff0c;AMOLED屏幕的最大优点之一便是在显示黑色或深色颜色时更加节能&#xff0c;但事实的确如此吗&#xff1f;而从Greenhot给出的结果来看…

计算机省电模式在什么地方,计算机打开并显示省电模式应该怎么办

打开计算机电源后,始终会显示省电模式,然后屏幕始终是黑色,并且无法访问系统。如果在打开计算机电源时显示省电模式,该怎么办?今天,我将讨论打开计算机电源时的省电模式解决方案。方法。 计算机打开时显示的省电模式解决方案1 ​​ 1)显示器电源关闭,或显示器数据电缆接…

Linux内核省电社区,【整理】Linux禁用CPU省电状态/Linux系统性能调优 | 勤奋的小青蛙...

为了提升CPU性能&#xff0c;我们要禁用CPU省电状态。 我们强烈推荐禁用CPU的省电模式&#xff0c; 进行此更改可能会导致功耗增加&#xff0c;但会提高稳定性和性能。 那么&#xff1a;如何在Redhat或Suse Linux系统上禁用CPU节能状态&#xff1f; 对于RedHat Linux(版本7及更…

计算机主机省电模式,打开计算机显示屏后即显示省电模式,如何处理

2442396444 本文适用于HP和Compaq台式计算机使用的HP和Compaq平板显示器。 计算机启动&#xff0c;关闭或进入节能模式时&#xff0c;显示屏上会显示一条消息。显示的信息可能是&#xff1a; 进入睡眠状态(正在进入睡眠模式) 未检测到信号(未检测到信号) 同步超出范围(同步超出…

计算机关闭节能模式,bios怎么关闭cpu节能模式_bios节能模式怎么设置

BIOS是英文“Basic Input Output System”的缩略词&#xff0c;直译过来后中文名称就是“基本输入输出系统”。在IBM PC兼容系统上&#xff0c;是一种业界标准的固件接口。BIOS这个字眼是在1975年第一次由CP/M操作系统中出现。BIOS是个人电脑启动时加载的第一个软件。 其实&…

802.11 Power Save(节电/省电/节能)机制总结

背景介绍 了解WIFI节能的机制之前&#xff0c;首先要了解WIFI能耗的消耗发生在什么状态下&#xff0c;显而易见&#xff0c;发送和接收肯定是消耗能量的&#xff0c;还有一点是要认识到的&#xff0c;Power Save通常是在STA端进行的&#xff0c;AP需要一直广播并且很多AP都是固…

ctfshow-web-红包题第七弹

0x00 前言 CTF 加解密合集CTF Web合集 0x01 题目 0x02 Write Up 首先上来访问就是phpinfo。常规思路先扫一下目录。 发现一个.git文件403&#xff0c;这种情况通常都是存在文件夹&#xff0c;但是不能直接访问文件夹导致的。那么我们可以使用git_extract工具进行获取内容。…

SIP 协议路由规则详解

文章目录 SIP 路由关键字段SIP 路由图解 SIP 路由关键字段 SIP 协议实际上和 HTTP 类似&#xff0c;都是基于文本、可阅读的应用层协议&#xff0c;二者的不同之处在于 SIP 协议是有状态的。在 SIP 协议中&#xff0c;影响报文路由的相关字段如下表所示&#xff0c;总结起来如…

字体试衣间--轻松管理你的字体

2007年10月07日 14:44:00 PS达人们一般都收集有很多素材&#xff0c;字体便是其中之一&#xff0c;而且还颇为重要。硬盘上存有几百种字体是常事&#xff0c;即使上千&#xff0c;也不奇怪。但这么多字体&#xff0c;管理起来就很成问题了。 因此&#xff0c;今天给大家推荐这个…