为了保证高并发,高性能,高可用性。当我们使用单机 Redis 时,将存在:
- 出现机器故障时,Redis 无法提供服务
- 会出现容量瓶颈
故,使用如下的三种方案去保证上述的三个特性。
主从复制
数据提供方:master,数据接收方:slave
主要是将 master 的数据复制到 slave 中去,提高 Redis 服务的读写吞吐量
主从复制的三个流程
slave 连接 master 。先建立连接(socket 连接),然后 master 同步数据到 slave ,最后进入执行命令传播阶段。
1.建立连接阶段
主从连接方式
- slave 端执行命令:slaveof masterip masterport
- 在执行启动 slave 节点命令的时候添加 :redis-server … --slaveof masterip masterport
- 在 slave 的配置文件中添加一行:slaveof masterip masterport
设置密码
- 在配置文件中设置: requirepass password
- 执行命令设置密码:config set requirepassword password
当 master 设置了密码,slave 需要添加 master 的密码:
- 通过 slave 执行命令:auth password
- 在 slave 的配置文件中添加一行:masterauth password
2.数据同步阶段
全量复制:master 通过 socket 发送 bgsave 得到的 *.rdb 文件到 slave 中去,slave 清空自己的数据,并读取 *.rdb 完成数据的恢复。
增量(部分)复制: master 在执行 bgsave 时,同时还在接收外界的指令,所以需要增量复制完成这期间接收的指令。slave 接收 master 发送的复制缓冲区信息,执行 bgrewriteaof,恢复全量复制期间接收到的新数据。
注意:
- 在加入 salve 时,master 会执行 bgsave 等对内存和 CPU 资源消耗的命令,故应在 master 相对空闲的时候加入 slave
- 如果在全量复制期间,复制缓冲区空间被使用完,那么 slave 会进行第二次全量复制,全量复制期间,复制缓冲区重新存储新的命令。如果再次出现缓冲区溢出,那么 master-slave 将陷入复制的循环中。故需要合理的设置复制缓冲区大小:repl-blacklog-size 1mb(默认情况)
3.命令传播阶段
服务器的运行 Id(runid):标识不同的服务实例
复制缓冲区:master 将修改数据的命令(按 AOF 方式来记录命令)按字节加入到缓冲区的 FIFO 队列
复制偏移量:不同的实例记录不同的 offset,master 也记录的有 offset,用于恢复网络出现问题的间的数据
命令传播阶段下的心跳机制
心跳机制,是为了让 master 与 slave 双方保持持续的连接。
master 心跳:
- 指令:ping;
- 周期: repl-ping-slave-period 10秒;
- 作用:判断 slave 是否在线
slave 心跳任务:
- 指令:replconf ack [offset]
- 周期:1秒
- 作用:向 master 汇报自己的偏移量,并获取最新的数据变更指令。判断 master 是否在线
当 slave 掉线数或延迟过高时,master 为保障数据稳定,将拒绝所有信息同步操作
配置中有:
For example to require at least 3 slaves with a lag <= 10 seconds use:
min-slaves-to-write 3
min-slaves-max-lag 10
即当 slave 存活数小于3,或所有 slave 延迟高于10s时,master 拒绝写入功能。其中延迟和存活数的确定,是通过 slave 心跳的 replconf ack 来计算的。
主从复制的常见问题以及解决方案
出现频繁的全量复制
Situation:master 中数据的量会不断的增加,如果 master 重启,runid发生变化,会导致对所有的 slave 做全量复制,会严重的影响效率,如何解决?
Plain: master 实例在关闭服务(shutdown )时,保存上次的 runid 到 *.rdb 文件中,使 slave 认为还是之前的 master。该方案为 redis 内部提供的。
Situation:当 master 与 slave 之间网络情况不佳,且复制缓冲区出现 slave 的 offset 越界,触发 slave 的全量复制,导致 slave 无法对外提供服务问题。
Plain:针对项目实际情况,修改复制缓冲区大小(repl-backlog-size 1mb),其容量最好为 2 * 平均重连时间 * master每秒写入命令数据量
多个 slave 之间的数据不同步的问题
Situation:当网络信息波动或数据发送延迟时,会导致多台 slave 中的数据不一致问题
Plain:尽量让多台 redis 实例在同一个网段中。通过 slave 发送的 offset 判断延迟情况,决定是否关闭 slave 的数据访问
哨兵模式
哨兵是一个分布式系统,用于对 主从复制集群 中的每台服务器进行监控,当 master 宕机后,通过投票选择出新的 master 节点并为其接入 slave 节点。
哨兵也是 redis 服务器,只是他们不提供数据服务。哨兵数一般为单数。
启动哨兵服务: redis-setinel [ sentinel*.cof ]
如何在主从复制集群中,启动哨兵集群?
在主从复制的集群启动完成后,直接使用 redis-setinel *.conf 启动哨兵服务,哨兵就会对 *.conf 中配置的 master 进行监控,多台哨兵会自动识别,不需要额外的配置。
哨兵模式工作的三个流程
1.监控阶段:
对各个哨兵、master、slave进行监控,获取他们的信息
2.通知阶段:
在哨兵集群启动完成后,哨兵之间消息发布订阅的过程,保持相互间的连通。
3.故障转移阶段:
当一台哨兵发现 master 下线后,通知其他哨兵去判断 master 是否下线,当超过配置文件中配置的数量时,哨兵集群认定 master 已下线,进入下一步流程。
按照投票的机制,在哨兵集群中,选出一台去处理故障转移的节点,进入下一步流程。
选出的哨兵将在所有的 slave 中,选出一台作为新的 master ,其选取原则是 在线的、响应命令快的、与 master 断开时间最短的、offset最大或runid最小的一台 slave。哨兵向选定后的新 master 发送 slaveof no one ,向其余的 slave 发送 slaveof [newMasterIp newMasterPort]。至此,便完成了故障转移。
集群
集群的出现,是为了再次提高 Redis 的 QPS、整体容量等瓶颈问题。
集群的结构设计思想
集群中数据的存取:
对 key 使用一致性Hash算法,得到 key 存在槽位置(共16384个)以及所在的 redis 实例,然后到对应的 redis 去操作数据。
集群中内部通讯
集群中的每台机器,都存储了其他所有机器的槽信息。客户端每次在获取数据时,如果命中数据,直接返回;如果未命中,告诉客户端去哪台机器找数据,故操作数据时,最多发生两次请求。
集群的搭建
每个 redis 配置文件中添加如下指令:
cluster-enabled yes:标识这是集群中的一个节点
cluster-config-file *.conf:设置集群当前节点的配置文件名(可根据情况忽略)
cluster-node-timeout 10000:设置超时10秒后,认定节点下线
redis 的 src 目录下 redis-trib.rb ,必须通过该文件去启动集群:
./redis-trib.rb create --replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381
指定集群内部结构。1表示一台 master 接入一台 slave;ip:port 是所有的 master 实例。
在执行上面的命令后,会给出集群的构建信息,如主从关系、主节点的 Hash 槽范围、redis 实例的 runid等。
集群中数据操作需要注意
要使用 redis-cli -c *.conf 去连接 redis ,避免出现槽位置无法找到的问题。
集群中节点下线的处理
slave节点下线情况
当 slave 节点下线后,集群中会标记该 slave 节点下线,对外的整体服务没有太大影响。当该 slave 节点上线后,会自动接入到集群中
master节点下线情况
当 master 节点下线后,slave 周期为1s的心跳机制会根据设置的 cluster-node-timeout 去判定 master 是否下线。如果判定为下线,通知集群中其他节点,标记原来的 master 为 fail 状态,slave 将自己更换为 master 。当一段时间后,原 master 上线了,将被作为 slave 节点加入到集群中。