00:00
接下来我们就进入缓存与分布式所的学习章节,那通过以前我们的性能优化与压力测试经验,我们发现对于一些复杂的业务接口,我们可以通过优化它的业务逻辑来实现增加它的吞吐量,但在更多的时候,对于一些复杂的业务,我们已经不能通过简单的优化业务逻辑,调整它一些性能参数来极大的增加它的吞吐量。比如我们一些复杂的查询功能,特别我们这个菜单,我们这个菜单呢,我们经常要用这个数据,只要一来首页就要访问,访问量呢非常大,但是这个数据呢,我们一般不修改,它又不是经常变的,而每次都来查数据库呢,就会显得非常麻烦,所以我们就可以来引入缓存,在我们分布式系统里边,我们一定要合理使用缓存,那就能极大的提升我们系统的性能,那最终就是我们的数据库只承担我们数据落盘工作。也就是。
01:00
承担持久化工作,它只需要把数据持久化保存到一个地方,那为了我们系统加速访问,我们可以在第一次查出这个数据以后就把它放入我们缓存里边。以后我们需要数据,我们就直接从缓存中获取,没必要进行那一堆复杂的查询、计算、检索。这样呢,就能极大的提升我们系统的性能,所以在后来我们经常要使用缓存,那如何正确的合理的使用缓存,这就非常重要了,那首先我们得想清楚哪些数据适合放入缓存,比如我们这两大特点呢,第一个及时性和数据一致性要求不高的,特别是大家购物的时候,一些物流状态信息,那你可能呢,你十分钟看一次,50分钟看一次,你频率看的再高,它更新的速度非常慢,它不是每走一米它就要更新一下,我们对它的及时性的要求也不高,所以呢,物流状态信息我们也就适合放入缓存里边,这是第一方面,第二方面数据一致性要求不高的,好,我们提出了一个概念叫一致性,所谓的一致性,那就是我数据库里边存的这个数据和我最终读取出来的这个数据一不一样,特别针对于修改,比如我们数据库里边修改了商品的分类。如果我们使。
02:21
用了缓存,我们没有去来改缓存,那可能读取到的分类还是以前的分类,这个呢我们就叫不一致,但是呢,像商品分类这种,我们一致性要求不高,不高不是说不需要一致性,而是不需要很快的就到达一致性状态。比如我们数据库里边商品的分类已经改了,可能三秒、五秒乃至于三分钟以后,我们商品的整个分类数据缓存里边才能更新起来,那么三分钟以后呢,才能看到结果,我们从缓存里边才能拿到最新的数据,这样我们也是可以来接受的,因为这些数据呢,并不是那么重要。
03:01
所以呢,这是我们第一个及时性和一致性要求不高的数据,第二个访问量大,并且更新频率不高的数据,比如我们的商品,我们商品呢,一旦录入以后,我们商品的介绍信息也好,基本属性也好,我们都不再去做一些修改了,或者是我们很少去做一些修改,那这个时候呢,我们经常要查询商品数据,我们就可以把商品的信息放到缓存里边,因为商品的访问量呢特别大,每一次都来查数据库,这是一个非常慢的操作。所以把握住我们这两点,也就是总结起来,我们及时性,也就是我们的实时性要求不高的,数据一致性要求不高的,包括呢,我们读多写少的数据等等,我们都可以来加入缓存,而且如果使用缓存的话,我们一般是通过这种方式来使用缓存的,比如我们要读取一个数据,我们请求过来要读取数据,我们呢,就应该先从缓存中看一下有没有我们需要的这个数据,如果我们缓存命中了,那么就直接返回结果,我们就结束了,如果缓存里边没有这个数据,我们才需要查一次数据库,那数据库里边将查到的数据我们再放入缓存里边,把这个结果呢返回给我们档次请求,那下次我们再来请求,由于上一次呢,已经从数据库里边查到的数据给我们返回的同时,还把数据放到缓存里边了,所以下一次再来查询,我缓存里边就有数据了。
04:34
那这样呢,我们就能使用到我们整个的缓存,这就是当我们在读取数据的时候,我们缓存可以这么来使用,包括我们的伪代码,那就应该找的是这个样子,我们数据呢,就应该优先从缓存中加载看点load,比如我们按照ID从缓存中找,如果找到了那就直接返回,如果没找到,我们才需要从数据库中加载,而且呢,加载完了一定记得把数据呢给缓存中放一份,这样呢以后的请求进来就能使用了,所以呢,未来这就是我们在系统里边使用我们缓存进行读取数据的一个流程。但说起缓存,我们拿什么东西来作为缓存呢?缓存的技术有很多,最简单的缓存方式,那就是大家学的map,比如我们给map里边放一个数据,我们以后呢,要获取这个数据,先看map里边有没有,如果有了直接返回,如果没有了。
05:31
我们查一遍数据库,查到了再给map中一放,所以呢,如果我们来改造我们的业务逻辑,那最快的方式,比如呢,我们可以给这个类商,我们来加一个缓存的这个map,我来写一个map key呢就等于string值呢就是object对象。比如我们就叫catch,我们想缓存数据等于new一个哈希map,那只要以后。我们想要读取数据,我们都可以从缓存中先来加载,比如我们这个大型的复杂业务逻辑,它呢是获取我们这个catallo JS,那我们就可以来写一个代码,就应该是这样,如果我们从换缓存中来获取,比如它的K就叫这个方法名看到log杰森,因为我们要放一个数据,我们K呢就叫看log杰森,如果从缓存中获取的这个数据等等,那是空的,那说明没有,没有我们才有必要来进行下边这一堆的操作。
06:29
而如果有,比如我们把这一块来写完,如果我们缓存中有,我们来看一下。把这一块的缓存拿来,我们写的代码呢,最终就是这个样子走。这是缓存中得到的,它那么存的是什么类型的数据,我们可以直接来强转过来,比如我们要返回这个,好,我们给map里边也存一个map,这也没啥问题,我们来把它强转过来,也就说这是我们第一步,如果缓存中有就用缓存的。
07:03
那么就应该直接在这来return我们的catalog杰森好。我们一会儿来写这句return,如果没有,我们才需要调用我们这一堆的业务逻辑,我们把这个业务逻辑呢,我复制过来。我们放在这儿。那这一块呢,最终返回一个我们这个parent cid,就是我们要返回出去的数据,这是从数据库里边查到的,那在返回之前呢,为了让下一次缓存我们能用到这个数据。我们就应该把它再放到缓存中,所以这是我们查到的parent cid返回的同时,我们给缓存中放一份put,而且put的时候呢,我们K也用的是这个叫catallo杰森,我们之前获取用什么能放就用什么,把这个呢们重新再放进去,那下一次呢就可以使用到了。那么有缓存,缓存里边有,那就直接返回,缓存的没有,我们来查一遍再来返回,这就是我们来写的代码走。
08:05
这不是说缓存用最简单的,我们可以用一个map来做,而且呢,这样做肯定更快,因为map里面存的所有东西都是在内存中的,但是呢,我们把这种方式我们称为一个叫本地缓缓存,但是本地缓存呢,会出现一些问题,比如我们来举一个例子。首先我们来说一下什么是本地缓存,那本地缓存呢,就是我们这个缓存的组件,我们这个缓存组件当前是一个看跟我们这个代码呢,属于同一个进程的,也就是说他们运行在同一个项目里边,在同一个GVM里边。只相当于在本地保存一个副本的好,我们可以称为本地缓存,但本地缓存呢,我们可以来看一下,假设我们这有一个商品服务,我们获取分类,获取品牌,获取商品等等这些数据呢,我们经常要用,我们这些数据还经常不修改,一旦录入以后基本就固化了。那如果我们每次查询都要走数据库,很慢,我们可以选择把这些数据查出以后,给缓存中放一份,这样以后别人请求进来,只要是要这些数据,都可以先从缓存中看,缓存中有就给他返回,没有呢,我们就再查一遍,查出数据了,放到缓存里边,然后再返回,下次呢就再能查出来了。但如果我们使用本地缓存模式,那就是我们这些缓存的这个组件,我们这个map跟我们这个都在同一个项目里边部署着,那会有什么问题呢?如果我们这个应用是一个单体应用,永远只部署在一台机器,什么问题都没有,而且很快。
09:44
但是呢,我们在分布式系统下,我们一般是这样,那商品服务呢,可能会部署十几个服务器,那这样呢,每一个服务器里边都自带了一个自己的本地缓存,那这样呢,就会出现这么一个问题,比如我们举一个最简单的例子,我第一次请求我负载均衡来到第一个项目,第一个项目查数据的时候呢,缓存中没有,他查了一次数据库,查到以后呢,再放到缓存中了,那缓存中现在有数据了,那如果我们下次请求还能负载均衡到第一个项目,那我们直接从缓存中拿就行了,那如果我们没有负载到第一个项目,然后我们来到了第二个我们微服务,那我们这个微服务里边缓存现在还是没有数据,那还得查一遍数据库,如果第三次我们来到第三个项目,我们第三个项目的缓存里边还是没有数据,我们还是得查询数据库,所以呢,这是第一种问题。
10:42
比如我们这样一些缓存是分开的,各顾各的,那没有的话呢,都得自己查一遍,当然第二个问题是最复杂的问题,如果我们对数据进行了修改,比如我们三级分类数据改了一下,那为了能读取到正确的数据,我们一般性呢还要改一下缓存里边的数据,那这样的话呢,假设我们第一次修改请求来到了我们一号服务器,我们把这个分类数据修改了,我们把它的缓存改了,但是呢,二号三号服务器之前缓存里边的这个数据呢,我们又没法改。
11:14
因为我们负载均衡是来到一号的,那这样以后所有的请求,只要是定位到二号,三号服务器的,他拿到的数据呢,就跟一号服务器拿到数据是完全不一样的,所以这就产生了我们说的数据一致性的问题,所以呢,要解决这个问题,那我们最终的方式就应该是这样的,在我们分布式情况下,不应该再来使用本地的这些缓存,那在分布式系统下,我们该如何使用缓存?首先我们这个本地模式的缓存,它会出现很多问题,我们就应该不去这样使用缓存,我们的缓存使用方式应该是这样的,我们再来使用缓存的时候,这是每一个微服务,比如这是我们商品服务,当然也有其他的各种服务,我们商品服务呢,会缓存很多的数据,我们可以将所有商品服务缓存的数据呢,我们都可以放到一个我们称为缓存的中间件里边,大家都给集中的一个地方来缓存数据,这样呢。
12:15
我们比如负载均衡请求来到第一个服务器,第一个服务器来看缓存中没有它呢,就会给我们查出数据,查出以后呢,返回给我们,并且给缓存中放一份,那以后呢,负载均衡再来过来,就算来到第二个服务器,由于第一个服务器之前给缓存中已经放过了,所以我们第二个服务器就可以直接从缓存中拿到我们这个数据,那就无需调用我们复杂的业务逻辑了,包括第三个服务我们也可以来直接来拿,同样的,如果我们数据发生了修改,比如呢,我们有一次请求来到三号服务器,这是一次修改请求,那三号服务器呢,除了修改我们数据库外,他修改完成以后呢,也将缓存里边的数据做了一个更新,这样呢,即使别的请求来到其他服务器,由他们操作缓存,都是去操作一个共同的指定地方的缓存,所以就不会出现我们说的数据不一致的问题。这是我们在分布式情况下的缓。
13:15
存使用不应该把缓存放在它本地的进程里边,而是大家共享一个集中式的缓存中间件,那这个缓存中间件呢,我们就可以有很多种选择方案,比如大家最常用的red,而且呢,中间键的好处就是如果我们装了一台red,我们系统的缓存的数据量不够,或者性能也不足,我们可以让red做一个整个集群工作,包括呢,也可以让它分片存储,也就是这个red,比如我们来存一到1万号数据,这个呢,存1万到2万号数据。那这样呢,我们容量也会做一个理论上无限量的提升,所以打破了我们本地缓存的容量限制,而且呢,使用我们这个集中的缓存中间键,我们缓存使用起来也简单,而且我们单独来维护我们整个的缓存中间键,我们也可以做到高可用,高性能,所以呢,我们以后的缓存使用,我们就来使用red作为缓存,我们以后所有的微服务开发,我们都将数据缓存到red里边,我们自己呢,就不在自己的类里边来写我们这个map了,把这个呢我住掉,那以前的这个伪代码业务逻辑,我就把这一块都提出来,还是回到我们以前的方式,把这一块代板呢,我们来剪切过来。
14:36
这是我们说缓存这样用好,我们真正的以前的内容,我把这个拿过来,我们把这个缓存这一块拿过来好。以前呢就是这样,我们先呢去缓存中看有没有,如果缓存中有返回缓存中的,如果缓存中没有,我们这个就是查询数据库。虽然真正调用我们这个业务,调用业务也不是查询数据库了,咱我们业务执行之前直接来调用我们业务,那业务执行完了以后呢,将返回数据,返回数据又。
15:10
放入缓存。这是我们以前使用缓存的话呢,我们的使用模式,我就把这个注掉。那么以后呢,都使用red来作为我们的整个缓存服务,那通过这个分析,我们下一节课呢,我们就来整合red作为我们整个所有未来的未来所有的微服务的缓存组件。
我来说两句