视频:https://www.bilibili.com/video/BV12E411C7uD?p=1
redis集群脑裂:
redis读写分离:https://blog.csdn.net/qq_34021712/article/details/72026313
代码:
---
数据库和缓存的双写的不一致:
先更新数据库再删除缓存,可能失败了在删除缓存。
---
场景:修改商品库存,删除缓存。下次去缓存就读不到数据。
先删除缓存,再修改数据库,如果删除缓存成功了,如果修改数据库失败了,那么数据库中是旧数据,缓存中是空的,那么数据不会不一致,因为读的时候缓存没有,则读数据库中旧数据,然后更新到缓存中。
---
问题:在更新一个库存的时候,同时读取这个数据库的缓存。还是会出现不一致。就是你删除了缓存,还没来得及更新数据库,此时再来一个请求,读取的是旧的数据,理论上应该是在上一个之后的。
解决办法:
优化:
更新数据的时候,根据数据的唯一标识,将操作路由之后,发送到一个jvm内部的队列中。
读取数据的时候,如果发现数据不在缓存中,那么将重新读取数据+更新缓存的操作,根据唯一标识路由之后,也发送同一个jvm内部的队列中,一个队列对应一个工作线程,每个工作线程串行拿到对应的操作,然后一条一条的执行。
这样的话,一个数据变更的操作,先执行,删除缓存,然后再去更新数据库,但是还没完成更新,此时如果一个读请求过来。
读到了空的缓存,那么可以先将缓存更新的请求发送到队列中,此时会在队列中积压,然后同步等待缓存更新完成。
这里有一个优化点,一个队列中,其实多个更新缓存请求串在一起是没意义的,因此可以做过滤,如果发现队列中已经有一个更新缓存的请求了,那么就不用再放个更新请求操作进去了,直接等待前面的更新操作请求完成即可,待那个队列对应的工作线程完成了上一个操作的数据库的修改之后,才会去执行下一个操作,也就是缓存更新的操作,此时会从数据库中读取最新的值,然后写入缓存中,如果请求还在等待时间范围内,不断轮询发现可以取到值了,那么就直接返回; 如果请求等待的时间超过一定时长,那么这一次直接从数据库中读取当前的旧值。
问题1:
问题2:机器别的如何工作的。商品的写操作和读操作要路由到一个队列里面去。
---37---
代码:
要替换的代码
spring.datasource.url=jdbc:mysql://192.168.202.140:3306/eshop
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
@Bean
public JedisPool jedisPoolConnect() {
JedisPool jedisPool = new JedisPool(new GenericObjectPoolConfig(),"192.168.202.128", 6379,2000,"123456");
return jedisPool;
}
@Resource
private JedisPool jedisPoolConnect;
@Override
public void set(String key, String value) {
Jedis jedis = jedisPoolConnect.getResource();
jedis.set(key, value);
//jedis必须关闭
jedis.close();
}
@Override
public String get(String key) {
Jedis jedis = jedisPoolConnect.getResource();
try {
return jedis.get(key);
} finally {
//jedis必须关闭
jedis.close();
}
}
测试:http://localhost:8080/getCachedUserInfo
http://localhost:8080/getUserInfo
---39---
代码:
这个类就是一个单例的阻塞队列的集合
单例的线程池
这节课的思路。
开始就new一个单例的线程池,里面十个线程。
每一个线程放一个容量为100的阻塞队列泛型的Request,放在callable里面提交任务。
同时创建一个单例的RequestQueue,里面维护一个阻塞队列的集合,这个集合也是单例的。
---40---
代码:
第一步:删除缓存更新数据库
第二步:缓存为空,重新加载商品库存的缓存
---41---
代码:
第一步:
这个就是路由的。
第二步:我们看下更新商品库存的整个流程
第三步:我们看下获取商品库存的流程
---42---
代码:
两个优化:
1.读请求过来发现已经有一个写请求和一个读请求可就不需要压入队列了,因为写请求肯定会更新数据库,读请求肯定会读取最新的数据刷入缓存,自己只要是hang一会就可以从缓存中读到数据。
更新数据库的话就是我先删除的缓存再更新数据库
缓存的话就是我先查缓存没有的话再去数据库查再放在缓存
---
队列中有读的操作则不用执行了,前面是写的话要执行。
---43---
测试:
异步串行。
---44---
---45---
代码:
---46---
代码;
each搭建。
第一步:
第二步:
---47---
之前给大家讲解过,多级缓存架构,缓存数据生产服务,监听各个数据源服务的数据变更的消息,得到消息之后,然后调用接口拉去数据
将拉去到的数据,写入本地ehcache缓存一份,spring boot整合,演示过
数据写入redis分布式缓存中一份,你不断的将数据写入redis,写入redis,然后redis的内存是有限的,每个redis实例最大一般也就是设置给10G
那如果你不断的写入数据,当数据写入的量超过了redis能承受的范围之后,改该怎么玩儿呢???
redis是会在数据达到一定程度之后,超过了一个最大的限度之后,就会将数据进行一定的清理,从内存中清理掉一些数据
只有清理掉一些数据之后,才能将新的数据写入内存中
1、LRU算法概述
redis默认情况下就是使用LRU策略的,因为内存是有限的,但是如果你不断地往redis里面写入数据,那肯定是没法存放下所有的数据在内存的
所以redis默认情况下,当内存中写入的数据很满之后,就会使用LRU算法清理掉部分内存中的数据,腾出一些空间来,然后让新的数据写入redis缓存中
LRU:Least Recently Used,最近最少使用算法
将最近一段时间内,最少使用的一些数据,给干掉。比如说有一个key,在最近1个小时内,只被访问了一次; 还有一个key在最近1个小时内,被访问了1万次
这个时候比如你要将部分数据给清理掉,你会选择清理哪些数据啊?肯定是那个在最近小时内被访问了1次的数据
2、缓存清理设置
redis.conf
maxmemory,设置redis用来存放数据的最大的内存大小,一旦超出这个内存大小之后,就会立即使用LRU算法清理掉部分数据
如果用LRU,那么就是将最近最少使用的数据从缓存中清除出去
对于64 bit的机器,如果maxmemory设置为0,那么就默认不限制内存的使用,直到耗尽机器中所有的内存为止; 但是对于32 bit的机器,有一个隐式的闲置就是3GB
maxmemory-policy,可以设置内存达到最大闲置后,采取什么策略来处理
(1)noeviction: 如果内存使用达到了maxmemory,client还要继续写入数据,那么就直接报错给客户端
(2)allkeys-lru: 就是我们常说的LRU算法,移除掉最近最少使用的那些keys对应的数据
(3)volatile-lru: 也是采取LRU算法,但是仅仅针对那些设置了指定存活时间(TTL)的key才会清理掉
(4)allkeys-random: 随机选择一些key来删除掉
(5)volatile-random: 随机选择一些设置了TTL的key来删除掉
(6)volatile-ttl: 移除掉部分keys,选择那些TTL时间比较短的keys
在redis里面,写入key-value对的时候,是可以设置TTL,存活时间,比如你设置了60s。那么一个key-value对,在60s之后就会自动被删除
redis的使用,各种数据结构,list,set,等等
allkeys-lru
这边拓展一下思路,对技术的研究,一旦将一些技术研究的比较透彻之后,就喜欢横向对比底层的一些原理
storm,科普一下
玩儿大数据的人搞得,领域,实时计算领域,storm
storm有很多的流分组的一些策略,按shuffle分组,global全局分组,direct直接分组,fields按字段值hash后分组
分组策略也很多,但是,真正公司里99%的场景下,使用的也就是shuffle和fields,两种策略
redis,给了这么多种乱七八糟的缓存清理的算法,其实真正常用的可能也就那么一两种,allkeys-lru是最常用的
3、缓存清理的流程
(1)客户端执行数据写入操作
(2)redis server接收到写入操作之后,检查maxmemory的限制,如果超过了限制,那么就根据对应的policy清理掉部分数据
(3)写入操作完成执行
4、redis的LRU近似算法
科普一个相对来说稍微高级一丢丢的知识点
redis采取的是LRU近似算法,也就是对keys进行采样,然后在采样结果中进行数据清理
redis 3.0开始,在LRU近似算法中引入了pool机制,表现可以跟真正的LRU算法相当,但是还是有所差距的,不过这样可以减少内存的消耗
redis LRU算法,是采样之后再做LRU清理的,跟真正的、传统、全量的LRU算法是不太一样的
maxmemory-samples,比如5,可以设置采样的大小,如果设置为10,那么效果会更好,不过也会耗费更多的CPU资源
---48---
zk和kafka集群搭建
理论
多级缓存的架构
主要是用来解决什么样的数据的缓存的更新的啊???
时效性不高的数据,比如一些商品的基本信息,如果发生了变更,假设在5分钟之后再更新到页面中,供用户观察到,也是ok的
时效性要求不高的数据,那么我们采取的是异步更新缓存的策略
时效性要求很高的数据,库存,采取的是数据库+缓存双写的技术方案,也解决了双写的一致性的问题
时效性不高的缓存数据生产服务,监听一个消息队列,然后数据源服务(商品信息管理服务)发生了数据变更之后,就将数据变更的消息推送到消息队列中
缓存数据生产服务可以去消费到这个数据变更的消息,然后根据消息的指示提取一些参数,然后调用对应的数据源服务的接口,拉去数据,这个时候一般是从mysql库中拉去的
消息队列是什么东西?采取打的就是kafka
涉及的这种架构,对时效性要求高和时效性要求低的数据,分别采取什么技术方案?数据库+缓存双写一致性?异步+多级缓存架构?大缓存的维度化拆分?
你要关注的,是一些架构上的东西和思想,而不是具体的什么mq的使用
activemq的课程,书籍,资料
kafka集群,zookeeper集群,先搭建zookeeper集群,再搭建kafka集群
kafka另外一个原因:kafka,本来就要搭建zookeeper,zookeeper这个东西,后面我们还要用呢,缓存的分布式并发更新的问题,分布式锁解决
scala安装:https://blog.csdn.net/u010590120/article/details/94758430
kafka安装:https://blog.csdn.net/xxb249/article/details/100990743
语句:
nohup ./bin/kafka-server-start.sh -daemon config/server.properties &
nohup ./bin/kafka-server-stop.sh -daemon config/server.properties &
bin/kafka-topics.sh --zookeeper 192.168.202.128:2181,192.168.202.129:2181,192.168.202.130:2181 --topic test123 --replication-factor 2 --partitions 2 --create
bin/kafka-console-producer.sh --broker-list 192.168.202.128:9092,192.168.202.129:9092,192.168.202.130:9092 --topic test123
bin/kafka-console-consumer.sh --bootstrap-server 192.168.202.128:2181,192.168.202.129:2181,192.168.202.130:2181 --topic test123 --from-beginning
bin/kafka-topics.sh --list --zookeeper 192.168.202.128:2181
bin/kafka-topics.sh --create --zookeeper 192.168.202.128:2181 --topic first --partitions 2 --replication-factor 2
bin/kafka-console-producer.sh --topic first --broker-list 192.168.202.128:9092
bin/kafka-console-consumer.sh --topic first --bootstrap-server 192.168.202.128:2181
---49---
整合测试:
报错加:
spring.thymeleaf.check-template=false
spring.thymeleaf.check-template-location=false
代码:
bin/kafka-topics.sh --zookeeper 192.168.202.128:2181,192.168.202.129:2181,192.168.202.130:2181 --topic cache-message --replication-factor 1 --partitions 1 --create
Created topic cache-message
bin/kafka-console-producer.sh --broker-list 192.168.202.128:9092,192.168.202.129:9092,192.168.202.130:9092 --topic cache-message
这个服务就是一个
---50---
---51---
---52---