Redis 集群概述
单台 Redis 服务器可能遇到的问题
- 单个 Redis 服务器会发生 单点故障, 并且一台服务器需要处理所有的请求负载,压力较大 (容错性差)
- 从容量上,单个 Redis 服务器的内存容量有限,就算一台 Redis 服务器内容容量为 256G,也不能将所有内容作为 Redis 存储内容。
一般来说,单台Redis最大使用内容不应该超过 20G
基本描述
- 高可用 (High Availability):通常来描述一个系统经过专门的设计,从而减少停工时间,而保持其服务的高可用。
- 高并发(High Concurrency):通过设计保证系统能够同时并行处理很多请求
通常指标有:
- 响应时间(Response Time)
- 吞吐量(Throught)
- 每秒查询率 QPS (Query Per Second)
- 并发用户数
- 等
响应来说,牺牲了 一致性
提升系统的并发能力
提升系统并发能力的方式,主要有两种:垂直扩展(Scale Up)与水平扩展(Scale Out)。
# 垂直扩展(升硬件)
垂直扩展:提升单机处理能力。垂直扩展的方式只有两种
- 增强单机硬件性能,如:
添加 CPU 核数如 32 核,升级更好的网卡如万兆,升级根号的硬盘如SSD,扩充硬盘容量如 2T,扩充系统内存如 128G - 提升单机架构性能,如:
使用 Cache 来减少 IO 次数,使用异步来增加单服务吞吐量,使用无锁数据结构来减少响应时间
如果预算不是问题,单机扩展往往最自接,快捷
但有一个致命的不足,单机性能总是有限的。
所以分布式架构设计高并发终极解决方案还是水平扩展
# 水平扩展(分压力)
水平扩展:只要增加服务器数量,就能线性扩充系统性能。
水平扩展对系统架构设计是有要求的,难点在于:如何在架构各层进行可水平扩展 的设计
Redis 主从复制
简介
一个 Redis 服务可以有多个该服务的复制品,这个 Redis 服务成为 Master
其他复制称为 Slaves
如图:我们将一个 Redis 服务器作为主库(master),其他三台作为从库(Slave),主库只负责写数据,每次有数据库更新的数据同步到它所有的从库,而从库只负责读数据。
两个好处:
- 读写分离:提高服务器的负载能力,可根据读请求的规模自由增加或减少从库的数量
- 可用性:数据被复制成好几份,一台机器故障,可以快速从其他机器获取数据并回复。
在 Redis 主从模式中,一台主库可以拥有多个从库 ,但是一个从库只能隶属于一个主库。
工作流程
# 建立连接阶段 slaveof(或 replicaof)
启动从服务器
设置 master 的 地址和端口,保存 master 信息
# --port 6380 指定从库服务的 端口号
# --slaveof 127.0.0.1 6379 指定主服务器
/usr/local/redis/bin/redis-server /usr/local/redis/redis.conf --port 6380 --slaveof 127.0.0.1 6379
加上 slaveof 参数启动另一个 Redis 实例作为从库,并且监听 6380 端口
登录从服务器
/usr/local/redis/bin/redis-cli -p 6380 -a root
主从变换
# 不是任何的从
slaveof no one
# 指定从属的主服务器
slaveof ip地址 端口号
# 数据同步阶段 psync
数据同步阶段 master 说明
- 如果master 数据量大,同步阶段应该避免高峰期,避免master 阻塞,影响业务正常执行
- 复制缓冲区大小设定不合理,会导致数据溢出。
如果进行全量复制时,部分复制的缓存区被被填满,会出现指令丢失情况
这种情况下,在进行完成复制后,会进行第二次全量复制(如果部分复制的缓存区一直被填满,一直出现指令丢失,会导致slave陷入死循环状态)
避免这种情况,需要修改主服务器配置:repl-backlog-size 1mb
,来调整复制缓存区的大小。
改多少合理?
计算机性能决定
master 单机内存占用主机内存的哔哩不应过大,建议使用50%~70%
的内存,留下30%~50%
内存用于执行 bgsave命令和创建复制缓冲区
数据同步阶段 slave 说明
- 为了避免 slave 进行全量复制、部分复制时,服务器响应阻塞或数据不同步,建议关闭此期间的对外服务
slave-server-stale-data yes|no
- 数据同步阶段,master发送给slave信息可以理解为 master 是 slave的一个客户端,主动向 slave 发送命令
- 多个 slave 同时对 master 请求数据同步,master 发送的 RDB 文件增多,会对宽带造成巨大冲击。如果master带宽不足,数据同步需要错峰
(后面会讲技术方面的解决方案) slave
过多时,建议调整拓扑结构时,由一主多从结构变成树状结构,中间的节点即是master,也是slave。
注意,使用树状结构时,由于层级深度,导致深度越高的 slave 与 最顶层的 master 间数据同步延迟较大,数据一致性差,应谨慎选择
问题解决
- PSYNC 失败:-NOMASTERLINK Can’t SYNC while not connected with my master
127.0.0.1:6380> PSYNC
Entering replica output mode... (press Ctrl-C to quit)
SYNC with master failed: -NOMASTERLINK Can't SYNC while not connected with my master
这个问题,有两个可能性
- 你的主服务器自定义了密码
那么从服务器在连接时要指定主服务器的密码
- 主服务器设置成了 slave 模式(从服务器)
登录客户端,用slaveof no one
命令改回来
master_link_status:down
同步不成功,并且在info里面发现,master_link_status:down
要么是上面的情况,要么是防火墙没开
打开防火墙端口命令
# 端口看你自己情况
firewall-cmd --zone=public --add-port=3679/tcp --permanent
firewall-cmd --reload
# 命令传播阶段
这个阶段就一句话:保证实时数据同步
- 当 master 数据库状态被修改后,导致主从服务器数据状态不一致,此时需要让主从数据同步到一致的状态,同步的动作成为 命令传播
- master 将接收到的
数据变更命令
发送给 slave,slave 接收命令
后执行命令
。
部分复制(的细节)
- 命令传播阶段出现了断网现象和处理
- 网络闪断:忽略
- 短时间网络中断:部分复制
- 长时间网络中断:全量复制
- 部分复制的三个核心要输(下面一个个说)
- 服务器的运行 id (run id)
- 主服务器的复制积压缓冲区 (也就是前面简称的:复制缓冲区)
- 主主从服务器的复制偏移量
1. 服务器运行ID(runid)
- 概念:服务器运行 id 是每台服务器每次运行的身份识别码,一台服务器多次运行可以生成多个运行 id
- 组成:运行 id 由 40 位字符组成,是一个随机的十六进制字符
例如:
1b26e30907309452dede588893477e847cfcedd4
ffffffffffaaaaaaaaaaeeeeeeeeee1111111111 (神选id 🐶) - 作用:运行 id 被用于在服务器间进行传输,识别身份
如果想两次操作均对同一台服务器进行,必须每次操作携带对应的运行id,用于对方识别 - 实现方法:
运行id
在每台服务器启动时自动生成
master在首次连接slave时,会将自己的运行id
发送给slave,slave保存此运行id
(通过 info Server 命令,可以查看节点的runid
)
2、 3 . 复制积压缓冲区 + 偏移量
(如图)master 会将 部分复制的命令
放在 复制缓冲区
。
当 slave 1 断网,缓冲区会等待玩过重新连接后,才把数据发出
主从复制工作流程
复制缓冲区
概念:是一个先进先出(FIFO)的队列,用于存储服务器执行过程的命令
由来:每台服务器启动时,如果开启AOF或者被成为master节点,即创建复制缓冲区
(因此,我们通常不会吧AOF关闭)
内部组成:偏移量+字节值
工作原理:
- 就是通过 offset 判断同的 slave 当前数据传输到什么程度了
-
master
记录已发送的信息对应的 offset -
slave
记录已接收的信息对应的 offset
# 总结:数据同步+命令传播阶段工作流程(详细)
心跳机制
进入命令传播阶段, master 与 slave 间需要进行信息交换,使用心跳机制进行维护,实现双方连接保持在线
master 心跳:
- 指令:PING
- 周期:由
repl-ping-slave-period
决定,默认10s - 作用:判断 slave 是否在线
- 查询:INFO replication(获取slave最后一次连接时间间隔,lag项维持在0或1视为正常)
slave 心跳任务 - 指令:REPLCONF ACK [offset]
- 周期:1秒(比master相对快)
- 作用1:汇报slave自己的复制偏移量,获取最新的数据变更指令
- 作用2:判断master是否在线
心跳阶段注意事项
- 当 slave 多数掉线,或延迟过高时,master为保障数据稳定性,将拒绝所有信息同步操作
如:
min-slaves-to-write 2
min-slaves-max-lag 8
slave 数量少于2个,或者所有slave的延迟都大于等于10秒时,强制关闭master写功能,停止数据同步。
- slave 数量由 slave 发送 REPLCONF ACK 命令做确认
- slave 延迟由 slave 发送 REPLCONF ACK 命令做确认
# 总结:工作全流程(详细)
部分复制、全量复制看上面
# 主从复制常见问题
1、master 重启后的全量复制
一旦master重启,runid发生改变,会导致全部slave的全量复制。
解决方案
redis内部解决了,不需要我们设置(了解)
- 在 master 关闭时执行指令 shutdown save ,进行 RDB 持久haunted,将 runid 与 offset 保存到 RDB 文件中。
(下图,可以通过redis-check-rdb
进行查看) - master 重启后加载 RDB 文件恢复数据
2、频繁的全量复制
(前面提及)
- 问题现象:网络不佳,出现网络中断,slave 不能提供服务
- 原因:复制缓冲区过小,断网后 slave 的 offset 越界,触发全量复制
- 最终结果:slave 反复进行全量复制
- 解决方案:修改缓冲区大小
repl-backlog-size
- 建议设置如下:
- 测算 master 到 slave 的重连平均时长
second
- 获取 master 平均每秒产生写命令数据总量
write_size_per_second
- 最优复制缓冲区空间 =
2 * second * write_sieze_per_second
3、频繁的网络中断 (1)
4、频繁的网络中断(2)
5、数据不一致
哨兵模式(主机“宕机”怎么办)
- 将宕机的 master 下线
- 找到一个 slave 作为 master
- 通知所有的 slave 连接新的 master
- 启动新的 master 与 slave
-
全量复制 * N
+部分复制 * N
# 哨兵
哨兵(sentinel) 是一个分布式系统,用于对主从结构中的每台服务器进行 监控,当出现故障时通过 投票机制 选择新的 master 并将所有的 slave 连接到新的 master
作用:
- 监控
不断的检查 master 和 slave 是否正常运行
master 存活检测、master 与 slave 运行情况检测 - 通知(提醒)
- 自动故障转移
# 启动哨兵模式
配置哨兵
- 配置 一拖二 的主从结构
- 配置三个哨兵(配置相同,端口不同)
参看 sentinel.conf - 启动哨兵
redis-sentinel sentinel-端口号.conf
哨兵配置文件(sentinel.conf)的位置
把配置复制到 /usr/local/redis
目录
cp /opt/redis-5.0.8/sentinel.conf /usr/local/redis/sentinel26379.conf
查看 sentinel.conf 配置
# grep -v "#" 去掉注释
# grep -v "^$" 去掉换行
cat /opt/redis-5.0.8/sentinel.conf | grep -v "#" | grep -v "^$"
# 端口
port 26379
# 守护模式启动
daemonize no
# pid 文件位置
pidfile /var/run/redis-sentinel.pid
# 日志文件位置
logfile ""
# 数据保存位置
dir /tmp
# (核心) 语法:sentinel monitor (自定义)名字 masterIP master端口 多少个哨兵
# 最后的 2 是指,当有两个哨兵检测到 master 宕机,就认为 master 宕机
# 通常设置为 哨兵数量/2 + 1 (因此,哨兵通常设置单数个)
sentinel monitor mymaster 127.0.0.1 6379 2
# master 多长时间没响应,认为检测到 master 宕机 (名字与上面自定义名字相同)
# 默认 30秒
sentinel down-after-milliseconds mymaster 30000
# 新master上任后,允许的同时同步数据量
# 如果是 1 ,表示同时只能 1 台slave同步新的master数据
sentinel parallel-syncs mymaster 1
# 同步时间超过多少认为是超时(毫秒)
# 默认 3分钟
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
修改配置
如果 master 没有自定义密码,那么不需要修改配置
如果 自定义了密码,需要添加:
# 语法:sentinel auth-pass 配置中的master名 master密码
sentinel auth-pass mymaster 1
然后,同样的文件复制两份并修改全部端口
(下面可以用 shell 命令快捷完成)
sed 命令解释
# 同时复制并替换出一份26380配置的配置文件
sed 's/26379/26380/g' sentinel26379.conf >sentinel26380.conf
# 同时复制并替换出一份26381配置的配置文件
sed 's/26379/26381/g' sentinel26379.conf >sentinel26381.conf
启动
先起 master (起了就不用再起)
再起 slave
最后起哨兵
# 没有开后台,需要起三个窗口,不喜欢就开后台
/usr/local/redis/bin/redis-sentinel /usr/local/redis/sentinel26379.conf
/usr/local/redis/bin/redis-sentinel /usr/local/redis/sentinel26380.conf
/usr/local/redis/bin/redis-sentinel /usr/local/redis/sentinel26381.conf
哨兵客户端连接
/usr/local/redis/bin/redis-cli -p 26379
连接之后只能执行 哨兵对应的命令
info
这时,我们再看 哨兵的配置文件,可以发现配置文件中自动添加了 salve 的信息
# 问题 :开了多个哨兵,相互不能识别
哨兵是会相互自动识别的,不识别唯一的可能是ID重复了
把配置里面默认的 id 注释了就好了
# 模拟master宕机(日志分析,流程分析)
我们把 master kill 了
# 哨兵工作原理
主从切换
哨兵在进行主从切换过程中经历三个阶段
- 监控
同步信息 - 通知
保持连通 - 故障转移
发现问题
竞选负责人
选人新的 master
新 master 上任,其他 slave 切换 master,原master 作为 slave 故障恢复后连接
下面看每一步的细节
阶段一:监控阶段
用于同步各个节点的状态信息
- 获取各个 sentinel 的状态(是否在线)
- 获取 master 的状态
各个 slave 的详细信息
master 属性(runid、role:master) - 获取所有 slave 状态(根据 master 中的 slave 信息)
slave 属性(runid、role:slave、master_host、master_port、offset、…)
阶段二:通知阶段
一个 sentinel 间建立长期信息对等的阶段
sentinel 间形成“朋友圈”网络,各自 检测 master、slave 的工作状态,并把检测到的状态在“朋友圈”中发布
阶段三:故障转移阶段
如果 sentinel 发现无法连通 master 会在内部标记 断开记录 S_DOWN
(主观下线:猜测挂了)
在 sentinel 的“朋友圈” 发布 master 断开信息
(如下图)
其他 sentinel 看到 信息会去 “围观” master
把围观结果也发上 sentinel 的 专属“朋友圈”
如果有半数(可自定义)的sentinel 在 “朋友圈” 说 master 挂了,就会把 master 状态表示 从 S_DOWN
(主观下线:猜测挂)改为 O_DOWN
(客观下线:确认挂了)
(如下图)
sentinel 知道 master 挂了,要选取出新的 master
选举出 新 master 之前。sentinel 间要先选出一个 “主导者”(主导新master的诞生~)
因此,会先先投票选 主导的 sentinel
投票机制就是简单的 向其他sentinel 要票,最后票数过半的为主导者 (如果没有过半结果会一直重复投票,因此sentinel要单数!)
(如下图)
sentinel 独裁者决定 新 master 的流程(排除法)
- 排除不在线的
- 排除响应慢的
- 断开与原master断开时间久的(性能可能比较差)
- 看优先原则
- 排除优先级低的
- 排除 offset 前的
- 排除 runid 大的 (到这步,肯定只剩一个 slave 了,那人就当选master)
(下图)
选定 新 master 后,发送指令
- 向新的 master 发送 slaveof no one
- 向其他 slave发送 slaveof 新masterIP 端口
slave:你说你是哨兵,原来是罗马教皇锕。。