一、什么是缓存?
缓存就是数据交换的缓冲区(称作 Cache [ kæʃ ]
),是存贮数据的临时地方,一般读写性能较高。
重点:读写性能高,这也是缓冲被用作数据交换的缓冲区的原因。
比较典型的例子就是计算机,在计算机中典型的构造就是CPU、内存、硬盘。
CPU的运算能力发展到现在已经非常非常厉害了,这种运算能力已经远远的超过了内存和磁盘的读写数据的能力,但是CPU做的任何的运算都需要先从内存 / 磁盘中读到数据放到自己的寄存器才能做运算。
正是因为这种数据读写的能力远远低于CPU的运算能力,因此计算机性能就收到了一个限制,因此为了解决这个问题,人们就在CPU的内部添加了一个缓冲。CPU会把经常需要读写的一些数据放到CPU缓冲中去,当我们做告诉运算的时候,就不必要每次等半天,从内存和磁盘里把数据度过来再运算了,而是直接从缓冲中拿到一个数据进行一个运算,这样依赖就可以充分的让CPU的运算能力得到提升。
因此衡量一个CPU是否强大的一项标准就是CPU的缓存的大小,缓存越大,能缓存的数据自然也越多,那么处理起来的性能也就越好。
同样的,在我们Web应用开发的过程中也是离不开缓存的。
浏览器缓存:主要是存在于浏览器端的缓存,例如页面的静态资源(CSS、JS、图片),这些东西一般都是不变的,浏览器就可以把它缓存在本地,这样一来就无需每次访问都要去加载这些数据了,就可以大大的降低网络的这种延迟,提高页面的响应速度。
**应用层缓存:**浏览器中未命中的一些数据,就会到tomcat,也就是我们所编写的那些Java应用。而在Java应用中还可以添加 应用层缓存
,比如之前提到的map,或者是使用redis作为缓存,它读写速度非常快,因此用它来作为应用层缓冲再合适不过了。
**数据库缓存:**当redis缓冲未命中的情况下,请求依然还会落到我们的数据库,在数据库中有一片空间是 buffer pool
,增改查数据都会先加载到mysql的缓存中。
例如MySQL数据库是一个聚簇索引,它会给id创建索引,这些索引数据我们就可以把它缓存起来,这样一来当我们根据索引进行查询的时候,就可以在内存里快速检索得到结果,而不用每次都要去读写磁盘,效率也会大大提升。
当然最终数据查找还是要落到磁盘。做一些复杂的排序、表关联还会用到CPU去做运算。因此自然就会使用到CPU的L1,L2,L3级的多级缓存,甚至磁盘,它也可以去建立读写缓存。
所以在整个Web开发的每一个阶段,都可以去添加缓存,因此缓存的应用场景是非常丰富的。
但是缓存也不能乱用,任何东西都是一个双刃剑,有得必有失,当你引入缓冲后,它给你带来好处的同时也有会有一些成本,这一点我们就基于Web应用开发来分析一下,在Web应用中带来的一些作用和它的一个成本。
二、为什么要使用缓存
降低后端负载:请求进入tomcat后,以前我们是需要先去查数据库,而数据库本身是要做数据的磁盘读写,所以相对来讲效率是比较低的,导致我们整个业务的延迟也会比较高,特别是一些复杂的业务SQL,那么查询起来就更慢了,因此给数据库往往会带来一个比较大的压力,这个时候如果有了缓存,请求进入tomcat后,直接在缓存里查到数据返回给前端,不用去查数据库,这对于后端来讲压力就会大大降低了。
提高读写效率,降低反应时间:如果我们使用了缓冲,像redis读写延迟往往是在微妙级别的,这个显示时间大大缩短,读写效率大大提高,此时我们就能应对更高的并发请求了,因此在一些用户量比较大、并发比较高的情况下,使用缓存就能够去解决这些高并发问题了。
但是缓存也会增加代码复杂度和运营的成本:
数据一致性成本:数据本来是保存在数据库的,现在你把它缓存了一份放到内存中,例如redis,那用户查询的时候就会优先查询redis,这样一来减轻了数据库压力。但是如果数据库的数据发生了变化,而redis中的数据还是旧的数据,那么用户读取到的也就是旧的数据,此时两者就产生了不一致。如果是一些比较重要的数据,产生不一致甚至可能会带来一些比价严重的问题,所以这就是数据的一致性成本。
为了解决这个一致性问题,它就给我们的代码维护带来了极大的成本,因为解决的过程中需要有非常复杂的一些业务编码,而且在缓存一致性处理过程中,还会出现缓冲穿透、击穿等问题,这些问题又需要解决,于是代码复杂度也就越来越高。
运维成本:为了避免缓存雪崩这样的问题,还有保证缓存的高可用,缓存往往会需要搭建成集群模式,而缓存集群的这样的部署、维护就会有额外的人力上的一些成本。还有这些集群部署的过程中,还有一些硬件的成本。
由此可见,任何东西都是双刃剑,有得必有失。因此一个企业在开发过程中需要去选择使用缓存后带来的好处,能不能去弥补它所带来的一些成本,如果不能,那么我觉得不用也罢,特别是一些中小型的企业,刚刚起步,用户量也不大,这时候你不用缓冲其实也能应对日常的用户请求,那就没必要去做这件事情了。
![image-20240525161006793](https://img-blog.csdnimg.cn/img_convert/5e695e6238085d8ddded3561321eeb94.png)
前言:什么是缓存?
举个例子:越野车,山地自行车,都拥有"避震器",防止车体加速后因惯性,在酷似"U"字母的地形上飞跃,硬着陆导致的损害,像个弹簧一样;
同样,实际开发中,系统也需要"避震器",防止过高的数据访问猛冲系统,导致其操作线程无法及时处理信息而瘫痪;
这在实际开发中对企业讲,对产品口碑,用户评价都是致命的;所以企业非常重视缓存技术;
缓存(Cache),就是数据交换的缓冲区,俗称的缓存就是缓冲区内的数据,一般从数据库中获取,存储于本地代码(例如:
例1:Static final ConcurrentHashMap<K,V> map = new ConcurrentHashMap<>(); 本地用于高并发
例2:static final Cache<K,V> USER_CACHE = CacheBuilder.newBuilder().build(); 用于redis等缓存
例3:Static final Map<K,V> map = new HashMap(); 本地缓存
由于其被Static修饰,所以随着类的加载而被加载到内存之中,作为本地缓存,由于其又被final修饰,所以其引用(例3:map)和对象(例3:new HashMap())之间的关系是固定的,不能改变,因此不用担心赋值(=)导致缓存失效;
三、如何使用缓存
实际开发中,会构筑多级缓存来使系统运行速度进一步提升,例如:本地缓存与redis中的缓存并发使用