1. 主从复制
将一台redis服务器上的数据, 复制到其他redis服务器上. 前者称为master, 后者称为slaver. 数据的复制是单向的, 只能由主节点到从节点.
1.1 主从复制的作用
数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
读写分离:可以用于实现读写分离,主库写、从库读,读写分离不仅可以提高服务器的负载能力,同时可根据需求的变化,改变从库的数量;
高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
1.2 主从复制原理及过程
主从复制过程大体上分为三个阶段, 准备阶段(连接建立阶段), 数据同步阶段, 命令传播阶段。
1.2.1 设置主服务器的地址和端口
第一步, 从服务器要将主服务的地址和端口保存到自己服务器的masterhost和masterport属性里面。
1.2.2 建立socket连接 & 发送ping命令
建立socket连接
在执行slaveof命令后, 且设置好主服务器的地址和端口后, 会向主服务器发起套接字请求。
如果从服务器创建的套接字能成功连接(connect)到主服务器,那么从服务器将为这个套接字关联一个专门用于处理复制工作的文件事件处理器,这个处理器将负责执行后续的复制工作,比如接收RDB文件,以及接收主服务器传播来的写命令,诸如此类。
主服务器在接受(accept)从服务器的套接字连接之后,将为该套接字创建相应的客户端状态,并将从服务器看作是一个连接到主服务器的客户端来对待,这时从服务器将同时具有服务器(server)和客户端(client)两个身份:从服务器可以向主服务器发送命令请求,而主服务器则会向从服务器返回命令回复。
发起ping请求
连接建立成功后从节点发送 ping 请求进行首次通信,ping 请求主要目的如下:1. 检测主从之间网络套接字是否可用。2. 检测主节点当前是否可接受处理命令。(如果发送 ping 命令后,从节点没有收到主节点的 pong 回复或者超时,比如网络超时或者主节点正在阻塞无法响应命令,从节点会断开复制连接,下次定时任务会发起重连)
下图为启动从服务器所反馈的信息:
1.2.3 身份校验
从服务器在收到主服务器返回的pong回复后, 需要进行身份验证。如果从服务器设置了masterauth选项,那么进行身份验证。否则不进行身份认证。在需要进行身份验证的情况下,从服务器将向主服务器发送一条AUTH命令,命令的参数为从服务器masterauth选项的值。
可能遇到的情况有以下几种:
主服务器没有设置requirepass选项,从服务器没有设置masterauth,那么就继续后面的复制工作;
如果从服务器的通过AUTH命令发送的密码和主服务器requirepass选项所设置的密码相同,那么也继续后面的工作,否则返回错误invaild password;
如果主服务器设置了requireoass选项,但从服务器没有设置masterauth选项,那么服务器将返回NOAUTH错误。反过来如果主服务器没有设置requirepass选项,但是从服务器却设置了materauth选项,那么主服务器返回no password is set错误;
1.2.4 主从同步(同步 + 命令传播)
老版本同步

老版本同步缺陷:
没有断点续传的功能, 处于命令传播阶段的主从服务器因为网络原因而中断了复制,接着通过自动重连接重新连上了主服务器 此时需要从服务器原有的全部数据, 重新进行同步, 效率低下。
新版本同步(完整重同步:初次复制 + 部分重同步:断线后重复制)
部分重新同步有三个部分: 主从服务器复制偏移量, 主服务器的复制积压缓存区, 服务器运行id。
复制偏移量: 执行复制的主从服务器都会维护各自的复制偏移量. 主服务器每次向从服务器传播n个字节, 都会将自己的偏移量加n。从服务器接收主服务器传来的数据, 也会将自己的偏移量加n。
例子: 当前主服务器的复制偏移量是1000, 此时向从服务器传播30个字节数据, 结束后复制偏移量是1030. 从服务器没有收到30个字节数据, 从服务器的偏移量依然是1000. 主从数据不一致, 此时会向主服务器发送psync命令。
复制积压缓存区: 是一个固定长度, 先进先出的队列. 当主服务器进行命令传播的时候, 不仅会将命令给到从服务器, 也会给到缓存区. 当从服务向主服务器发送psync命令的时候, 需要将自己的复制偏移量带上, 主服务器可以通过复制偏移量和复制积压缓存区对比。
服务器运行id: 在初次主从复制的时候, 主服务器将自己的id发送给从服务器. 当从服务器断线重连接后, 从服务器将运行id发送给刚连接的主服务器. 若两者运行id一致, 则主服务器尝试部分同步, 若不一致, 主服务直接进行完整重同步。
命令传播
在完成同步之后,主服务器马上就接受到了新的写命令,执行完该命令后,主从的数据库状态又不一致。为了再次让主从数据库状态一致,主服务器就需要向从服务器执行命令传播操作 ,即把刚才造成不一致的写命令,发送给从服务器去执行。从服务器执行完成之后,主从数据库状态就又恢复一致了。
心跳检测(命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送命令) REPLCONF ACK
作用:
1. 检测主从服务器连接状态:
如果主服务器超过一秒钟没有收到从服务器发来的REPLCONF ACK命令,那么主服务器就知道主从服务器之间的连接出现问题了。
一般情况下,lag的值应该在0秒或者1秒之间跳动,如果超过1秒的话,那么说明主从服务器之间的连接出现了故障。
2. 检测命令丢失:
每发送一次这个命令(REPLCONF ACK offset) 从服务器都会向主服务器报告一次自己的复制偏移量。那此时尽管主服务器发送给从服务器的SET key value丢失了。也无所谓,主服务器马上就知道了。
3.辅助实现min-slaves配置选项:
Redis的min-slaves-to-write和min-slaves-max-lag两个选项可以防止主服务器在不安全的情况下执行写命令.
min-slaves-to-write 3 从服务器少于三个
min-slaves-max-lag 10 三个从服务器延迟(lag)都大于等于10.
1.3 主从复制实践:
1.在本机安装redis后,创建几个文件夹,用于存放redis.conf(不同从机器的配置) 
2.设置配置文件(端口 + 指向主节点)
3.启动服务器
4.客户端连接
5.主节点set值
1.4 主从复制实践(Docker版)
从仓库中拉取redis镜像(docker pull redis:latest) 注意: 通过镜像起的redis是没有redis.conf文件, 我们可以从官方下载(http://download.redis.io/redis-stable/redis.conf)。
我们打算搭建一个一主两备的集群,建了三个文件夹redis-main,redis-slave-1,redis-slave-1,我们将redis.conf模板拷贝到三个目录中, 并且修改其中的配置. ```
主服务器
bind 127.0.0.1 #如果bind选项为空的话,则允许所有来自于可用网络接口的连接
protected-mode no #保护模式,若为yes,只允许本地客户端连接 appendonly yes #开启后,Redis会把每次写入的数据在接收后都写入appendonly.aof文件,每次启动时Redis都会先把这个文件的数据读入内存里
从服务器
bind 127.0.0.1 #如果bind选项为空的话,则允许所有来自于可用网络接口的连接
protected-mode no #保护模式,若为yes,只允许本地客户端连接 appendonly yes #开启后,Redis会把每次写入的数据在接收后都写入appendonly.aof文件,每次启动时Redis都会先把这个文件的数据读入内存里 replicaof 10.12.254.162(宿主机ip) 6379
3. 使用docker启动redis服务:
— 主服务
docker run -p <容器端口>:<主机端口>
--name <容器名>-v <本地配置文件映射容器配置文件>:<本地文件夹挂载到容器文件夹>-d(表示以守护进程方式启动容器)<启动redis服务并制定配置文件(容器中的路径)>
docker run —name redis-main —privileged=true
-p 6379:6379
-v /Users/wangyanyang/Desktop/redis-demo/redis-master-slave/master:/data
-d redis /data/redis.conf
— 从服务 docker run —link redis-main:master —name redis-slave-1 —privileged=true -p 6380:6380 -v /Users/wangyanyang/Desktop/redis-demo/redis-master-slave/slave_1:/data -d redis /data/redis.conf docker run —link redis-main:master —name redis-slave-2 —privileged=true -p 6381:6381 -v /Users/wangyanyang/Desktop/redis-demo/redis-master-slave/slave_2:/data -d redis /data/redis.conf ```
- 进入redis查看


2. 哨兵模式(Sentinel,特殊的服务器)
我们可以将Redis Sentinel 集群看成是一个 ZooKeeper 集群,它是集群高可用的心脏, 它一般是由 3~5 个节点组成,这样挂了个别节点集群还可以正常运转。 它负责持续监控主从节点的健康,当主节点挂掉时,自动选择一个最优的从节点切换为主节点。客户端来连接集群时,会首先连接 sentinel,通过 sentinel 来查询主节点的地址, 然后再去连接主节点进行数据交互。当主节点发生故障时,客户端会重新向 sentinel 要地址,sentinel 会将最新的主节点地址告诉客户端。如此应用程序将无需重启即可自动完成节点切换.
2.1 哨兵实战:
1.复制哨兵的配置文件,到”各个服务器”下,并相应的进行配置(一般配置端口)

2.启动服务器 & 哨兵

3.模拟主机器断开, 哨兵会自动选举从机器当主节点, 如下所示:
- 哨兵会自动进行配置文件的修改(redis.conf & redis-sentinel.conf)

2.2 哨兵原理
2.2.1 监控
哨兵启动后, 会与监控的主服务器建立两条连接。一条用来订阅该主服务器的sentinel:hello频道以获取其他同样监控该数据库的哨兵节点的信息(订阅连接),另外一条连接来发送命令,并接受相应(命令连接):
1.每10秒哨兵会向主数据库和从数据库发送INFO命令.
好处: 发送INFO命令使得哨兵可以获得当前数据库相关信息(运行ID、复制等)从而实现新节点的自动发现。哨兵借助INFO命令来获取所有复制该主数据库的从数据库信息. 其次:启动时, 哨兵向主数据库发送INFO命令,通过解析返回结果来得知从数据库列表,然后对每个从数据库建立两个连接;其三:哨兵会每10秒定时向已知的所有主从数据库发送INFO命令来获取信息更新并进行相应的操作.
2.每2秒哨兵会向主数据库和从数据库发送sentinel:hello频道发送自己的信息
好处: 哨兵向主从数据库的sentinel:hello频道发送信息与同样监控该数据的哨兵分享自己的信息.
发送消息内容为<哨兵地址>,<哨兵端口>,<哨兵的运行ID>,<哨兵的配置版本>,<主数据库名字>,<主数据库地址>,<主数据库端口>,<主数据库配置版本>
3.每1秒哨兵会向主数据库、从数据库和其他哨兵节点发送PING命令
2.2.2 通知
配置完成后, 哨兵每秒会向主从服务器发送ping命令, 当超过down-after-milliseconds指定的时间后,如果被PING的数据库或节点没有进行回复,哨兵认为主观下线(当前的哨兵进程来看,该节点已经下线)。
当一个Sentinel将一个主服务器判断为主观下线后. Sentinel会向同时监控这个主服务器的所有其他Sentinel发送查询命令(sentinel is-master-down-by-addr) 其他哨兵给出自己的判断, 如果达到Sentinel配置中的quorum数量的Sentinel实例都判断主服务器为主观下线,则该主服务器就会被判定为客观下线(ODown)。
2.2.3 故障转移(failover)
在确定主服务器下线后, 会进行内部投票, 选择一个领头羊. 故障恢复由该领头羊操作。
1.发现主数据库客观下线的哨兵节点(A节点)向每个哨兵节点发送命令,要求对方选自己成为新的领头哨兵。
2.如果目标哨兵节点没有选择过其他哨兵,则同意A成为领头哨兵
3.如果A节点发现有超过半数且超过quorum参数值的哨兵节点同意自己成为领头羊哨兵,那么A成功成为领头羊哨兵
4.当有多个哨兵节点同时参选领头羊哨兵,则会出现没有任何节点当选的可能。此时每个参选节点将等待一个随机时间重新发起参选请求,进行下一轮选举,直到成功为止
5.选出领头羊哨兵之后,领头羊哨兵开始对主数据库进行故障恢复,内容如下:
5.1)所有在线的从数据库中,选择有限级最高的从数据库。优先级通过slave-priority来设置
5.2)如果有多个最高优先级的从数据库,则复制命令的偏移量越大越优先
5.3)如果条件都一样,则选择运行ID较小的从数据库
选出一个从数据库后,领头羊哨兵向从数据库发送replicaof no one命令使其升级为主数据库。而领头羊向其他从数据库发送repliaof命令使其成为新主数据库的从数据库。最后进行更新内部记录,将已经停止服务的旧主数据库更新为新的从数据库身份继续服务。

