**课程目标**
* 能够理解Redis的主从复制架构
* 能够理解Redis的Sentinel架构
* 能够理解Redis集群架构
## Redis的主从复制架构 44
1. Redis可以配置主从复制结构,主节点就是Master节点,从节点就是Slave,Slave会不断地从Master节点同步数据
2. 一个Master节点可以对应多个Slave节点
3. Slave节点会向Master节点发送SYNC的请求,Master接收到SYNC请求之后,做两件事情:(初始化)
1. 第一、以RDB的形式保存快照
2. 第二、因为保存快照是比较耗时的,所以会将保存快照期间的命令缓存下来
3. 当RDB保存完整后,就开始发送给Slave节点开始同步数据
4. 一旦初始化完成,后续Master节点每接收一个写命令,就会立刻同步到Slave节点
4. 主从复制结构的应用场景
1. 备份容错(如果只有一个节点,会存在单点故障问题)
2. 读写分离(读多写少的场景很适用),如果写操作很多,就得使用集群
3. 从数据库持久化(可以将持久化的性能消耗移动到从节点)
### 另外两台服务器安装Redis 45
注意事项:
* 如果发现虚拟机无法联网,需要检查虚拟机的NAT网络配置,NAT的网络其实类似于家里的路由器,相当于Linux服务器要和Windows宿主机进行连接,就是通过这个路由器来连接的。所以网关地址都需要配置成NAT的网络
* 如果发现yum安装软件的时候下载速度比较慢,可以在网上搜索配置一个阿里云YUM源
* make test是表示执行测试用例,消耗的时间会比较长,大家晚上安装的时候可以跳过。因为这个Redis包已经在CentOS7.7做过测试了,没有兼容性问题
安装主从复制主要有一条配置:
slaveof master_ip master_port
### 启动Redis服务 47
要检查主从复制已经配置好了:
就是当启动node2.itcast.cn、node3.itcast.cn时,如果数据都已经同步过来了,就表示OK了。但往node1.itcast.cn写入数据,数据会马上同步到另外两个节点。
## Redis中的Sentinel架构 47
### Sentinel介绍 47
* 哨兵是主要用来保障Redis主从复制架构是高可用的,是能够自动进行主节点切换的
* 它可以监控主从复制中的节点,当主节点崩溃的时候,会自动进行切换
* 一般哨兵的配置节点数不能是1个,最好是有几个主从节点,就配置几个哨兵。不能哨兵自己出现单点故障
* 哨兵在Linux系统上是一个独立的进程,它的默认端口号是26379
* 当我们去查看操作哨兵的时候,需要指定客户端的连接端口号为:26379
### 配置哨兵 49
* 哨兵的配置文件是: sentinel.conf
* 启动哨兵是:redis-sentinel sentinel.conf
### Redis的sentinel模式代码开发连接 52
* 因为有了哨兵之后,哨兵能够自动进行主节点的切换,所以在Java代码中,为了同步实现高可用,就不能将主节点的地址写死,需要使用哨兵来进行连接,当哨兵进行主节点切换的时候,Java代码也能自动进行切换
```java
// JedisPoolConfig配置对象
JedisPoolConfig config = new JedisPoolConfig();
// 指定最大空闲连接为10个
config.setMaxIdle(10);
// 最小空闲连接5个
config.setMinIdle(5);
// 最大等待时间为3000毫秒
config.setMaxWaitMillis(3000);
// 最大连接数为50
config.setMaxTotal(50);
HashSet<String> sentinelSet = new HashSet<>();
sentinelSet.add("node1.itcast.cn:26379");
sentinelSet.add("node2.itcast.cn:26379");
sentinelSet.add("node3.itcast.cn:26379");
jedisSentinelPool = new JedisSentinelPool("mymaster", sentinelSet, config);
```
## Redis 集群 54
### 引言 54
* QPS——Query Per Second、TPS——Tranaction Per Second
* Redis集群解决的问题
* 高可用
* 解决单机Redis内存是有限的问题(技术组件不是内存越多越好,因为内存配置得越高,例如JVM的Heap内存配置得很高后,就会导致内存碎片整理很耗时,垃圾回收会发生卡顿,导致集群的效率下降)
* 解决单机Redis网络受限的问题
* 分布式存储的重点——分区
* MySQL——根据顺序分区的方式,例如:根据主键来进行分区(分库分表),一般是在Java web开发中会遇到
* 按照哈希取余的方式来进行分区(类似于MapReduce的默认分区策略)
* 问题:当分区的数量发生变化的时候,会导致key产生较大影响,原先分布在第一个节点上的数据,分区数量调整后,指定到了其他的分区
* 按照一致性Hash的方式来进行分区
* 是一个环状的Hash空间,它的分区算法是和哈希取余算法不一样的
* 首先将每一个分区的标号(0、1、2)进行算法计算,然后将计算出来的值,放入到环状的Hash空间空
* 再将key同样进行算法计算,然后将计算出来的值,同样也放入到环状的Hash空间中
* 最后,找到key在hash空间中距离自己位置最近的分区,放入到该分区中
* 这样,当分区的数量发生变化的时候,影响不会太大
* Redis集群是使用槽的方式来进行分区的
* 现有有一个槽的空间(0-16383),需要将这些空间分布到不同的节点中
* node1: 0 -3xxx
* node2: 3xxx- 6xxx
* ...
* 有一个key,首先进行CRC16算法&16383 = 值,Redis会判断这个值应该在哪个槽中
### Redis Cluster 设计 55
### Redis Cluster 搭建 57
集群规划:
因为Redis集群一般推荐的是有几个主,就需要有几个从,我们搭建的是三主三从。
node1.itcast.cn:7001、7002
node2.itcast.cn:7001、7002
node3.itcast.cn:7001、7002
在配置期间,我们只能去做一些基本配置,但是配置的时候需要开启Redis集群
```properties
cluster-enabled yes
# Every cluster node has a cluster configuration file. This file is not
# intended to be edited by hand. It is created and updated by Redis nodes.
# Every Redis Cluster node requires a different cluster configuration file.
# Make sure that instances running in the same system do not have
# overlapping cluster configuration file names.
#
# cluster-config-file nodes-6379.conf
cluster-config-file nodes-7001.conf
# Cluster node timeout is the amount of milliseconds a node must be unreachable
# for it to be considered in failure state.
# Most other internal time limits are multiple of the node timeout.
#
# cluster-node-timeout 15000
cluster-node-timeout 15000
```
* 为了方便搭建集群,绑定的IP地址是0.0.0.0,监听本台电脑所有能够被访问到的IP。127.0.0.1是仅仅表示本主机,外部是不能连接进来的。
* 配置完后,需要启动所有的端口Redis进程,一共有六个
* 启动好六个节点后,需要进行集群初始化
* 集群初始化会自动分配Master和Slave,我们可以指定一个Master可以对应几个Slave
* Redis还会自动分配槽
* 连接Redis集群集群
* redis-cli -c -h ip地址 -p 端口号
* 但我们访问某个key的时候,如果这个key不再本机,Redis集群会自动跳转到另外的机器
1. 问题解决办法
* 看Log(最好是把Log复制出来,找各种蛛丝马迹,异常:cause by:异常信息,调用栈:哪个类哪个方法出现的异常)
* StackoverFlow(上面有各种异常解决办法)
* 看源码(github去找到对应框架的版本,根据异常信息中的调用栈来查找问题)
2. 学习技术的三点
* 轻量级框架、技术组件(轻量级是要华少一点时间去学习各种API,因为此时没有应用场景)
* 重量级项目(将技术组件结合起来,切记:和业务场景结合在一起)
* 大师级面试题(提升自己的沟通表达能力、组织话术、熟记各种原理)
### 启动Redis服务 62
* 制作一键启动脚本
* 通过ssh在每个节点上执行启动命令、或者是shutdown命令
* 就是正常启动每一个redis集群中的节点就可以了,因为集群已经初始化了,每个节点都已经有固定的槽位
* 当我们搭建了Redis的集群后,不在需要哨兵了,Redis集群会自动完成主从切换
### Redis Cluster 管理 68
![image-20200907120953965](image/image-20200907120953965.png)
### JavaAPI操作redis集群 69
* JedisPool——操作单机版本的Redis
* JedisSentinelPool——操作哨兵系统的主从结构
* JedisCluster——操作Redis集群的
注意事项:
* 在构建JedisCluster的时候,需要将集群中的主、从节点所有节点都添加到Set里面
* 如果使用JedisCluster操作Redis时候,不再需要获取Redis连接,直接去操作Redis即可,因为JedisCluster已经封装好了对应的操作
```java
/*
* 创建一个HashSet<HostAndPort>,用于保存集群中所有节点的机器名和端口号
* 创建JedisPoolConfig对象,用于配置Redis连接池配置
* 创建JedisCluster对象
* 使用JedisCluster对象设置一个key,然后获取key对应的值
*/
public class RedisClusterTest {
private JedisCluster jedisCluster;
@BeforeTest
public void beforeTest() {
HashSet<HostAndPort> hostAndPortSet = new HashSet<>();
hostAndPortSet.add(new HostAndPort("node1.itcast.cn", 7001));
hostAndPortSet.add(new HostAndPort("node1.itcast.cn", 7002));
hostAndPortSet.add(new HostAndPort("node2.itcast.cn", 7001));
hostAndPortSet.add(new HostAndPort("node2.itcast.cn", 7002));
hostAndPortSet.add(new HostAndPort("node3.itcast.cn", 7001));
hostAndPortSet.add(new HostAndPort("node3.itcast.cn", 7002));
// JedisPoolConfig配置对象
JedisPoolConfig config = new JedisPoolConfig();
// 指定最大空闲连接为10个
config.setMaxIdle(10);
// 最小空闲连接5个
config.setMinIdle(5);
// 最大等待时间为3000毫秒
config.setMaxWaitMillis(3000);
// 最大连接数为50
config.setMaxTotal(50);
jedisCluster = new JedisCluster(hostAndPortSet, config);
}
@Test
public void setTest() {
jedisCluster.set("k2", "v2");
System.out.println(jedisCluster.get("k2"));
}
@AfterTest
public void afterTest() throws IOException {
jedisCluster.close();
}
}
```
## Redis高频面试题 71
### 缓存穿透 71
* 缓存穿透指的是一些黑色发送一些数据库和缓存都不存在的key,这样的话就是穿透了缓存、也穿透了数据库。高并发的情况下,就会压垮数据库,导致系统崩溃
* 解决方案
* 给非法的key也设置一些缓存,这样下一次发出请求的时候,就会直接查缓存
* 布隆过滤器(BloomFilter):布隆过滤器可以快速地过滤掉缓存中不存在的key,但是有一个问题:布隆过滤器不能准确地判断这个已经存在的key真的存在。
### 缓存击穿 72
* 击穿指的是key存在的,击穿了Redis,数据库有数据。大量的并发会把数据库拖垮
* 使用互斥锁来去解决
* 通过sexnx("互斥锁", 1)
* sexnx表示如果key不存在的时候,才会设置一个key,如果存在就直接返回
* 这种方式可以确保只有一个线程能够进入到加载数据库的逻辑中
### 缓存雪崩 72
* 缓存服务器中大量的key失效或者在服务器重启的时候,会出现大量并发去直接访问数据库,导致数据库的压力过大,系统崩溃
解决办法:
* 不能让所有的key集中在某一个时刻失效,可以将过期时间设置为随机
* 不然后台系统直接操作数据库,通过消息队列来隔离业务系统和数据
### 缩写
常见英文缩写:
message -> msg
1. 去重元音(a、e、i、u...)
2. 去除重复的字母
* 减少key的长度,提升Redis的效率
* 命名以:业务:模块:ID...