## 主从复制
Redis中可以将一台服务器设置主机(master),别的服务器设置成从机(slaver),实现`主从模式`。在Redis的主从模式下主服务器只能`写数据`,从服务器只能`读数据`。Redis的主从模式会将主服务器中的所有数据会通过`写时复制`技术**备份**到从服务器中,从而保证主从服务器之间的“数据一致性”。
```
备注:
主机:master - 写数据
从机:slaver - 读数据
```
将一台服务器设置为从服务器的命令为:
```bash
localhost:6380>slaveof master_ip port
# 例如:slaveof localhost 6379
# 即可将6380端口这台服务器设置为6379端口服务器的从服务器
```
使用`info replication`命令可以查看当前服务器的状态
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210529193805847.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDE4NDk5MA==,size_16,color_FFFFFF,t_70#pic_center)
接着查看主服务器的状态
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210529193822225.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDE4NDk5MA==,size_16,color_FFFFFF,t_70#pic_center)
Redis中搭建主从模式的命令非常简单,下面就来看主从复制原理和服务器宕机时保存数据的解决方案。
下面是Redis中主从复制原理的实现,在不同版本的其实现有些区别。
### 2.8版本之下的主从复制实现
Redis中的复制功能可以分为两个操作
1. `同步`:将从服务器的数据库状态更新为主服务器的数据库状态。
2. `命令传播`:主服务器的数据被修改之后,将**修改命令**发送到从服务器中修改对应的数据,保持数据一致性。
当从服务器的客户端向从服务器发送**slaveof**命令要求从服务器复制主服务器当中的数据时,从服务器会向主服务器发送`sync命令`来执行同步操作,会经过如下步骤:
1. 从服务器向主服务器发送sync命令。
2. 主服务器接收到sync命令之后执行`bgsave命令`,生成一个rdb文件。同时使用一个**缓冲区记录从现在开始执行**的所有写命令,这是为了在执行bgsave命令阶段和发送rdb文件阶段主服务器可能有数据被修改了,这一部分修改的数据会在后面通过命令传播的方式复制到从服务器中。
3. 当主服务器执行完bgsave命令之后,会将rdb文件发送给从服务器,从服务器根据这个rdb文件与主服务的数据库状态进行同步。(写时复制技术)
:-: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210529194140394.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDE4NDk5MA==,size_16,color_FFFFFF,t_70#pic_center)
在同步操作实现之后,每当主服务器的数据被修改了,就会通过命令传播的方式向从服务器**发送同样的命令**修改从服务器中数据,再一次的达到数据同步的效果。
> 2.8版本以下复制操作的缺陷
在2.8版本以下的复制操作中如果从服务器和主服务器断开连接了(注意不是宕机),再重新连接之后,从服务器会重新执行一遍同步操作,即还是重新发送`sync命令`。这种断线后重新复制的操作的效率是非常低下的。例如
```
在还没有断开连接之前,主从服务器都有键key1,key2,key3...key1000。
在从服务器断开连接到重新恢复连接阶段,主服务器又新加了键key1001,key1002,key1003,
而从服务器仍然之后前面的1000个键。如果这个时候因为这新加的三个键而重新完整的一份rdb数据,
是非常消耗性能和带宽的。
```
2.8及之上版本就对断开连接之后重新连接的复制进行了优化。
### 2.8版本及之上的主从复制实现(重点)
2.8及之上的版本使用**psync**命令来替代sync命令执行复制时的同步操作。psync命令分为完全重同步和部分重同步两种模式。
1. 完全重同步的实现与sync命令的实现基本一致。
2. 断开之后重新连接的数据同步通过`部分重同步模式`来实现。
部分重同步模式功能的实现有三个部分构成,**偏移量**,**复制积压缓冲区**,**服务器的运行ID**。
1. 偏移量 replication offset,主从服务器双方会维护一个复制偏移量,每次主服务器向从服务器传播N个字节的数据的时候就会在自己的偏移量上加上N。同时从服务器在接收到这N个字节之后也会在自己的偏移量上加上N。
通过对比主从服务器的偏移量大小,就很容易能够得出主从服务的数据是否一致。
从服务器向主服务器发送psync信号时会**携带自己的偏移量**过去。
2. 复制积压缓冲区,是由主服务维护的一个固定长度的队列,默认大小为1MB。当主服务进行命令传播时,不仅会将命令发送给从服务器,还会将命令保存进复制积压缓冲区中。因此队列中会保存最近的命令,同时在队列中会记录对应命令的偏移量。
当主服务器接收到从服务器的psync命令中的偏移量之后,判断从服务器的偏移量和自己当前的偏移量,如果不一致,就去复制积压缓冲区找找对应的偏移量对应的命令发送给从服务器执行。**如果偏移量在复制积压缓冲区没有对应,则会进行一次完全重同步操作。**
`复制积压缓冲区的大小可以去:repl-backlog-size中修改。`
3. 服务器运行ID(run ID),每个Redis服务器都有一个属于自己的40位随机的十六进制组成的ID编号。
当从服务器第一次与主服务进行同步时,会保存主服务器的id。接着在断线重连之后会发送主服务器的ID,然后主服务器判断该ID是否与自己的ID相等。如果相等则进行部分重同步的操作,如果不相等,则进行完全重同步操作。
> psync命令的格式
1. 从服务在第一次执行slaveof时,即开始第一次复制主服务器中的数据时会发送
```bash
psync ? -1
```
表示第一次复制,此时进行的是完整重同步。
2. 在之后的每一次发送的psync命令中都会发送
```bash
psync <runid> <offset>
```
runid:服务器id。
offset:数据偏移量。
完整的主从复制流程图如下:
:-: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210529194505288.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDE4NDk5MA==,size_16,color_FFFFFF,t_70#pic_center)
### 主从复制的三种常用结构
1. 一主两仆
如果一个从服务器挂掉了,重启之后还是作为一个独立的主服务器,而不会继续作为原来主机的从服务器。需要使用`slaveof ip port`命令重新设置。
如果是主服务器挂掉了,从服务器仍然是从服务器,主服务器重启之后仍然是主服务器。
2. 薪火相传
`常用`
类似于树的递归方式进行主从服务器的设置。当有一台从服务器断开了,可能会导致树的断开。
3. 反客为主
当主服务器挂掉了之后,其从服务器中有一台成为主服务器。使用命令:
```
slaveof no one
```
这种方式需要手动的进行设置。
> 检查网络连接状态
在命令传播阶段,从服务器每秒会向主服务发送一次
```bash
replconf ack <replication_offset>
```
这个命令可以起到:
1. 检测主从服务器之间的网络连接状态
在从主务器中通过info replication可以查看如下信息:
:-: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210529194600934.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDE4NDk5MA==,size_16,color_FFFFFF,t_70#pic_center)
`lag`就是发送命令的时间差,一般在0-1之间,如果超过1,主从之间的连接可能就断开了。
2. 辅助实现min-slaves选项
redis.conf中有如下两个文件可以避免主服务在不安全的情况下进行写操作:
```conf
min-slaves-to-write 3
min-slaves-max-lag 10
```
在从服务器数量少于3个,或者三个从服务器的延迟都大于10秒时,不进行写操作。
3. 防止命令丢失
在命令传播过程中可能会导致命令丢失,这个使用通过比较发送的replication_offset就可以发现,不一致的情况下主服务器就会从复制积压缓冲区中再发送一次。
【参考】
1. 《Redis设计与实现》第15章
- 第一章 Java基础
- ThreadLocal
- Java异常体系
- Java集合框架
- List接口及其实现类
- Queue接口及其实现类
- Set接口及其实现类
- Map接口及其实现类
- JDK1.8新特性
- Lambda表达式
- 常用函数式接口
- stream流
- 面试
- 第二章 Java虚拟机
- 第一节、运行时数据区
- 第二节、垃圾回收
- 第三节、类加载机制
- 第四节、类文件与字节码指令
- 第五节、语法糖
- 第六节、运行期优化
- 面试常见问题
- 第三章 并发编程
- 第一节、Java中的线程
- 第二节、Java中的锁
- 第三节、线程池
- 第四节、并发工具类
- AQS
- 第四章 网络编程
- WebSocket协议
- Netty
- Netty入门
- Netty-自定义协议
- 面试题
- IO
- 网络IO模型
- 第五章 操作系统
- IO
- 文件系统的相关概念
- Java几种文件读写方式性能对比
- Socket
- 内存管理
- 进程、线程、协程
- IO模型的演化过程
- 第六章 计算机网络
- 第七章 消息队列
- RabbitMQ
- 第八章 开发框架
- Spring
- Spring事务
- Spring MVC
- Spring Boot
- Mybatis
- Mybatis-Plus
- Shiro
- 第九章 数据库
- Mysql
- Mysql中的索引
- Mysql中的锁
- 面试常见问题
- Mysql中的日志
- InnoDB存储引擎
- 事务
- Redis
- redis的数据类型
- redis数据结构
- Redis主从复制
- 哨兵模式
- 面试题
- Spring Boot整合Lettuce+Redisson实现布隆过滤器
- 集群
- Redis网络IO模型
- 第十章 设计模式
- 设计模式-七大原则
- 设计模式-单例模式
- 设计模式-备忘录模式
- 设计模式-原型模式
- 设计模式-责任链模式
- 设计模式-过滤模式
- 设计模式-观察者模式
- 设计模式-工厂方法模式
- 设计模式-抽象工厂模式
- 设计模式-代理模式
- 第十一章 后端开发常用工具、库
- Docker
- Docker安装Mysql
- 第十二章 中间件
- ZooKeeper