目录
- Redis持久化与高可用
-
- [1. Redis持久化](#1. Redis持久化)
-
- [1.1 RDB持久化](#1.1 RDB持久化)
-
- [(1) 简介](#(1) 简介)
- [(2) rdb文件](#(2) rdb文件)
- [(3) save与bgsave](#(3) save与bgsave)
- [(4) rdb恢复-方式及优势](#(4) rdb恢复-方式及优势)
- [1.2 AOF持久化](#1.2 AOF持久化)
-
- [(1) 简介](#(1) 简介)
- [(2) aof配置](#(2) aof配置)
- [(3) aof持久化流程](#(3) aof持久化流程)
- [(4) aof重写](#(4) aof重写)
- [(5) aof文件恢复](#(5) aof文件恢复)
- [1.3 RDB与AOF的对比](#1.3 RDB与AOF的对比)
- [1.4 持久化问题优化](#1.4 持久化问题优化)
- 2、主从复制
-
- [2.1 简介](#2.1 简介)
- [2.2 搭建主从集群](#2.2 搭建主从集群)
- [2.3 主从复制原理](#2.3 主从复制原理)
-
- [(1) 复制的过程](#(1) 复制的过程)
- [(2) 数据同步](#(2) 数据同步)
- [2.4 主从复制相关参数](#2.4 主从复制相关参数)
- [2.5 主从复制虚拟实践](#2.5 主从复制虚拟实践)
- 3、哨兵模式
-
- 3.1简介
- [3.2 搭建](#3.2 搭建)
-
- [(1) 安装部署](#(1) 安装部署)
- [(2) 哨兵节点相关的配置](#(2) 哨兵节点相关的配置)
- [(3) sentinel部署技巧](#(3) sentinel部署技巧)
- [3.3 运维管理](#3.3 运维管理)
-
- [sentinel API](#sentinel API)
- 业务连接
- [3.4 哨兵实现原理](#3.4 哨兵实现原理)
-
- [(1) 三个定时任务](#(1) 三个定时任务)
- [(2) 主观下线和客观下线](#(2) 主观下线和客观下线)
- [(3) 故障转移](#(3) 故障转移)
- [3.5 主从切换测试](#3.5 主从切换测试)
-
- [(1) 手动进行故障转移](#(1) 手动进行故障转移)
- [(2) 强制主从切换](#(2) 强制主从切换)
- [4. Redis Cluster](#4. Redis Cluster)
-
- [4.1 Cluster简介](#4.1 Cluster简介)
-
- [(1) 数据分布hash](#(1) 数据分布hash)
- [(2) Redis数据分区](#(2) Redis数据分区)
- [4.2 Redis Cluster部署](#4.2 Redis Cluster部署)
-
- [(1) redis-cli部署](#(1) redis-cli部署)
- [(1) 新增master节点 192.169.9.78:6530](#(1) 新增master节点 192.169.9.78:6530)
- [(2) 集群重新sharding](#(2) 集群重新sharding)
- [(3) 验证集群的slot分配](#(3) 验证集群的slot分配)
- [新增slave节点 6511](#新增slave节点 6511)
-
-
- [(2) cluster相关配置](#(2) cluster相关配置)
- [4.3 Redis Cluster核心原理](#4.3 Redis Cluster核心原理)
-
- [(1) gossip通信流程](#(1) gossip通信流程)
- [(2) 请求路由](#(2) 请求路由)
- [(3) 故障发现](#(3) 故障发现)
- [(4) 故障转移](#(4) 故障转移)
- [4.4 Redis Cluster运维管理](#4.4 Redis Cluster运维管理)
-
- [(1) 操作命令管理](#(1) 操作命令管理)
- [(2) 人工故障转移测试](#(2) 人工故障转移测试)
-
Redis持久化与高可用
1. Redis持久化
持久化,顾名思义,是将内存同步到磁盘的过程。
redis支持三种持久化机制
- RDB持久化
- AOF持久化
- RDB与AOF混合模式
1.1 RDB持久化
(1) 简介
RDB是Redis中的一种持久化机制,用于将数据保存到硬盘上的二进制文件中。
redis有两种方式进行RDB持久化
- 手动触发
- 自动触发
(2) rdb文件
操作符
config set dir (newDir)
config set dbfilename (newFileName)
压缩
config set rdbcompression (yes|no)
校验
config set rdbchecksum (yes|no)
redis-check-dump #使用该工具检测RDB文件并获取对应的错误报告
文件示意图
- 文件头
- 文件内容
- 文件尾
文件头
redis魔数、rdb版本、redis的版本、操作系统位数、创建时间、使用内存、主从复制信息
文件内容
数据每个db以254开头
文件尾
lua脚本、使用的module、写入结束标识和校验和
(3) save与bgsave
- save:执行时会阻塞redis(之前讲过redis是一个循环,save执行1秒,所有命令全部阻塞1秒)
- bgsave:Redis会在后台通过fork异步进行快照操作,执行RDB备份的是子进程,主进程还可以继续进行命令操作。可以通过lastsave命令获取最后一次成功执行快照的时间
自动触发配置
# 表示90秒内有1个键改动,就会执行rdb
save 900 1
# 表示30秒内有10个键改动,就会执行rdb
save 300 10
# 表示60秒内有1万个键改动,就会执行rdb
save 60 10000
# redis通过L2库算法对RDB文件进行压缩,会消耗子进程的cpu资源(多核、物理核、逻辑核有区别),如果是单核就会影响主进程
rdbcompression yes
# redis默认使用CRC64的算法对RDB文件进行完整性校验
rdbchecksum yes
bgsave持久化流程
[父进程]
|
+-- 如果有其他子进程正在执行,直接返回
|
+-- fork
|
+-- [子进程]
|
+-- 3) 响应其他命令
bgsave命令的运作流程
(4) rdb恢复-方式及优势
-
方式
- 关闭Redis
- 先把备份的文件拷贝到工作目录下
cp dump2.rdb dump.rdb - 启动Redis,备份数据会直接加载
-
优势
- 适合大规模的数据恢复
- 对数据完整性和一致性要求不高更适合使用
- 节省磁盘空间
- 恢复速度快
-
劣势
- Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑
- 虽然Redis在Fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能
- 在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改
-
禁用
- 永久禁止RDB:
save ""# 停止周期性触发 - 动态停止:
redis-cli config set save ""#save后给空值,表示禁用保存策略
- 永久禁止RDB:
1.2 AOF持久化
(1) 简介
以日志的形式将所有写操作追加到文件的末尾,使得数据在文件中呈现出与Redis内存中相同的状态。
记录写操作命令:
AOF 会将过期时间由相对转成绝对时间;
# AOF为什么直接采用文本协议格式?
- 文本协议具有很好的兼容性
- 文本协议具有可读性,方便直接修改和处理
# aof文件的内容
- 客户端与服务器间使用RESP进行交互
例如 set hello world 这条命令,在AOF缓冲区会追加如下文本(格式化显示的结果):
*3 #参数数量为3个
$3 #set参数字节数分别是3
set
$5 #hello参数字节数分别是5
hello
$5 #world参数字节数分别是5
world
实际传输格式: *3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n
(2) aof配置
# aof相关配置
appendonly=yes #开启aof
appendfilename=appendonly.aof #配置aof的文件名,默认文件名是appendonly.aof
appendfsync=(everysec|always|no) #aof日志刷盘策略
# 动态设置
config set appendonly (yes|no)
config set appendfilename (newFileName)
config set appendfsync (everysec|always|no)
(3) aof持久化流程
AOF的工作流程操作:
- 命令写入(append)
- 文件同步(sync)
- 文件重写(rewrite)
- 重启加载(load)
流程示意图:
命令写入 -> AOF缓冲 -> 文件同步 -> AOF文件
|
v
重启加载
(4) aof重写
AOF文件重写是把Redis进程内的数据转化为写命令同步到新AOF文件的过程。目的是为了解决AOF文件体积过大的问题,以减少磁盘空间的占用和提高读取性能。而且更小的AOF文件可以更快地被Redis加载。
AOF重写触发方式
-
手动触发(直接调用
bgrewriteaof命令) -
自动触发(根据
auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机)aof重写相关配置
no-appendfsync-on-rewrite yes # aof重写期间,新进来的命令会暂存内存,等重写完了,会追加(如果出现宕机,重写启动到现在的数据会丢失)
aof重写条件,是原来aof的2倍,且最小文件是64mb
auto-aof-rewrite-percentage=100 #当前aof文件大小超过上一次重写的aof文件的100%进行重写
auto-aof-rewrite-min-size=64mb #重写AOF文件所需的最小文件大小为64mb
aof重写流程
bgrewriteaof
|
+-- redis主进程
|
+-- fork
|
+-- 子进程
|
+-- 旧AOF文件
|
+-- 通知主进程
(5) aof文件恢复
正常恢复
- 修改默认的
appendonly no,改为yes - 将有数据的aof文件复制一份保存到对应目录(查看目录:
config get dir) - 恢复:重启redis然后重新加载
异常恢复
- 修改默认的
appendonly no,改为yes - 如遇到AOF文件损坏,通过
/usr/local/bin/redis-check-aof --fix appendonly.aof进行恢复 - 备份被写坏的AOF文件
- 恢复:重启redis,然后重新加载
redis 启动加载顺序
启动
|
+-- 开启 AOF?
|
+-- no -> 存在 RDB?
| |
| +-- yes -> 加载 RDB
| |
| +-- no -> 存在 AOF?
| |
| +-- yes -> 加载 AOF
| |
| +-- no -> 启动成功
|
+-- yes -> 成功?
|
+-- yes -> 启动成功
|
+-- no -> 启动失败
1.3 RDB与AOF的对比
RDB持久化
- 二进制数据
- RDB文件紧凑 体积小
- 网络传输快
- 恢复速度快
- 无法做到实时
AOF持久化
- 纯文本
- 实时性好,支持秒级持久化
- 文件大
- 恢复速度慢
- 对性能影响大
rdb与aof对过期key的处理
- 过期key对RDB和AOF没有任何影响
- 过期key持久化到rdb之前,会检查是否过期,过期的key不进入RDB文件
- 过期key持久化到aof
- 当key过期后,还没有被删除,此时进行执行aof持久化操作,该key是不会进入aof文件的,因为没有发生修改命令
- 当key过期后,在发生删除操作时,程序会向aof文件追加一条del命令(在将来的以aof文件恢复数据的时候该过期的键就会被删除)
- RDB文件恢复时,数据载入数据库之前,会对key先进行过期检查,如果过期,不导入数据库(主库情况)
- aof重写时,会先判断key是否过期,已过期的key不会重写到aof文件
1.4 持久化问题优化
-
fork操作
对于ops几万的redis实例,fork秒级影响整体业务延时
如何改善
- 支持fork的虚拟化技术,避免使用xen或者物理
- 通过分片,降低每个redis实例的容量,控制在10gb以内,如果性能要求更高,继续降低
- 降低fork操作频率
-
cpu内存优化
- 不要和其他CPU密集型服务器部署在一起,造成CPU过度竞争
- 多实例部署,尽量保证同一时刻只有一个子进程执行重写
- 关闭THP
-
磁盘优化
- 高流量使用SSD持久化
- 分量存储分摊IO压力(redis 10多个实例都需要AOF持久化)
-
AOF追加阻塞问题优化
定位:- 日志输出:
Asynchronous AOF fsync is taking too long (disk is busy). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis - info Persistence统计中,
aof_delayed_fsync指标会累加 - 磁盘io监控
优化:
- 优化系统硬盘负载
- 日志输出:
2、主从复制
2.1 简介
主机数据更新后根据配置和策略,自动同步到备机的master/slave机制。
Master以写为主,Slave以读为主。
Redis的复制拓扑结构类型:
- 一主一从
- 一主多从
- 树状主从结构
目的:
- 容灾恢复
- 数据冗余
- 负载均衡
- 高可用
配置复制的方式有以下三种 :
1)在配置文件中加入 slaveof <masterHost> <masterPort> 随Redis启动生效。
2)在redis-server启动命令后加入 --slaveof <masterHost> <masterPort> 生效。
3)直接使用命令:slaveof <masterHost> <masterPort> 生效。
2.2 搭建主从集群
主:192.168.9.78:6410
从:192.168.9.78:6411
1、redis部署安装
(1)目录初始化
mkdir -p /data/redis/{6410,6411}
cp -r /usr/local/redis/bin/ /data/redis/6410/
cp -r /usr/local/redis/bin/ /data/redis/6411/
(2)生成配置文件
6410主节点
cat > /data/redis/6410/redis_6410.conf <<EOF
daemonize yes
timeout 0
databases 16
dir "/data/redis/6410"
slowlog-log-slower-than 10000
slowlog-max-len 128
hz 10
port 6410
maxmemory 100mb
appendonly yes
appendfsync everysec
appendfilename "appendonly-6410.aof"
dbfilename "dump-6410.rdb"
logfile "/data/redis/6410/redis_6410.log"
pidfile /data/redis/6410/redis_6410.pid
protected-mode no
requirepass ""
masterauth ""
EOF
6411从节点
cat > /data/redis/6411/redis_6411.conf <<EOF
daemonize yes
timeout 0
databases 16
dir "/data/redis/6411"
slowlog-log-slower-than 10000
slowlog-max-len 128
hz 10
port 6411
maxmemory 100mb
appendonly yes
appendfsync everysec
appendfilename "appendonly-6411.aof"
dbfilename "dump-6411.rdb"
logfile "/data/redis/6411/redis_6411.log"
pidfile /data/redis/6411/redis_6411.pid
protected-mode no
requirepass ""
masterauth ""
EOF
(3)启动
/data/redis/6410/bin/redis-server /data/redis/6410/redis_6410.conf
/data/redis/6411/bin/redis-server /data/redis/6411/redis_6411.conf
2、配置主从复制
-
从节点的配置文件增加一行:
slaveof 192.168.9.78 6410,然后重启即可生效,变为slave -
从节点在线使用命令挂载
127.0.0.1:6411> slaveof 127.0.0.1 6410 OK
3、查看主从复制的状态
127.0.0.1:6411> info Replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6410
master_link_status:up
4、验证
主节点:redis-cli -p 6410 set hello oldboy
从节点:redis-cli -p 6411 get hello 返回 "oldboy"
5、从节点设置断开主从复制
127.0.0.1:6411> slaveof no one
OK
127.0.0.1:6411> info Replication
role:master
从库切换主库
slaveof newmasterIP Port
流程:
- 断开旧主节点复制关系
- 与新主节点建立复制关系
- 删除从节点当前所有数据
- 对新主数据复制
2.3 主从复制原理
(1) 复制的过程
主从复制过程大体可以分为3个阶段:
- 连接建立阶段(即准备阶段)
- 数据同步阶段
- 命令传播阶段
主从复制过程大体可以分为6个过程:
- 从节点保存主节点(master)信息。
- 建立Socket连接
- 发送PING命令
- 权限验证
- 全量数据同步
- 命令持续复制
流程示意图:
slaveof 127.0.0.1 6380
|
+-- slave 6380
|
+-- 【1】保存主节点信息
+-- 【2】主从建立 socket 连接
+-- 【3】发送 ping 命令
+-- 【4】权限验证
+-- 【5】同步数据集
+-- 【6】命令持续复制
|
+-- master 6379
(2) 数据同步
同步过程分为:
- 全量复制
- 增量复制
psync命令运行需要以下组件支持:
- 主从节点各自复制偏移量
- 主节点复制积压缓冲区
- 主节点运行id
查看运行id:
127.0.0.1:6410> info server
run_id:aa7828c0cbdf62432ad3b4174bdaaa6dd2c9ba4c
复制偏移量维护
复制积压缓冲区示意图
全量数据同步
全量数据同步:先执行一次全同步,请求master BgSave出自己的一个RDB Snapshot文件发给slave,slave接收完毕后,清除掉自己的旧数据,然后将RDB载入内存。
全量复制流程
- 发送psync数据同步,psync -1 第一次
- 主节点解析全量复制,恢复
- 保存主节点ID和偏移量offset
- 主节点bgsave-->RDB------>本地-->从节点
- 数据量级较大rdb超过60s传输,调入repl-timeout
- 从节点清空数据-->接受RDB快照-->网络传输-->主节点积压增量命令数据-->接受RDB完毕-->接受增量积压命令数据
- 参数
client-output-buffer-limit slave 256MB 64MB 60: 60s持续大于64MB或者超过256MB,同步失败,需要调整参数(高并发场景)
- 参数
- 加载RDB文件
- 加载成功,且开启AOF则做bgrewriteaof操作
总结:全重复制开销
- 主bgsave
- rdb网络传输
- 从节点flushdata
- 从 load RDB
- AOF重写
流程示意图:
slave 6380
|
+-- 1) psync ? -1
|
+-- 2) +FULLRESYNC {runId} {offset}
|
+-- 3) save masterInfo
|
+-- 5) send RDB
|
+-- 6) send buffer
|
+-- 7) flush old data
|
+-- 8) load RDB
|
master 6379
|
+-- 4) bgsave
|
+-- 开启 AOF?
|
+-- no -> Done
|
+-- yes -> 9) bgrewriteaof
增量数据同步
增量数据同步:master作为一个普通的client连入slave,将所有写操作转发给slave,没有特殊的同步协议
增量复制流程
psync runid offset
场景:网络断连,命令丢失,从补充数据,主的缓冲区有命令直接发送给从流程
1. 主从网络断连,repl-timeout
2. 主节点缓冲命令,默认缓存1MB
3. 从节点网络恢复,再次连接主节点
4. 主从恢复,从节点根据 offset和主的ID psync部分复制
5. 主节点校验,如果有offset除命令少数据,发送给从
6. 从节点恢复数据
流程示意图:
1) Connection lost
|
+-- slave 6380
|
2) request
|
+-- master 6379
|
+-- 3) Connecting to master
|
+-- 4) psync {offset} {runid}
|
+-- 5) CONTINUE
|
+-- 6) send partial data
2.4 主从复制相关参数
slaveof <masterip> <masterport> #添加从节点
slave-serve-stale-data yes #同步数据期间,是否可使用陈旧数据向客户端提供服务
slave-read-only yes
repl-diskless-sync no #无盘复制适用于主节点所在机器磁盘性能较差但网络带宽较充裕的场景
repl-diskless-sync-delay 5 #两次diskless模式的数据同步操作的时间间隔
repl-ping-slave-period 10 #Slave节点向Master节点发送ping指令的事件间隔,10s
repl-timeout 60 #Master和Slave之间的超时时间
repl-disable-tcp-nodelay no #主从复制时使用的网络资源优化参数
#默认关闭
#关闭:所有命令数据发送从节点,延迟变少,但是网络带宽消耗增加。适合同机房
#开启:合并Nagle算法数据包,默认40ms,节省带宽增大主从延时,适合跨机房部署
repl-backlog-size 1MB #主节点复制积压缓冲区大小
slave-priority #当前Slave节点的优先级权重
min-slaves-to-write和min-slaves-max-lag #拒绝数据写操作的策略
2.5 主从复制虚拟实践
-
读写分离
缺点:写并发较高,多个从节点到主节点复制命令多次过度消耗网络带宽
优点:高并发读写分离
问题:
- 复制数据延迟:监控报警
- 过期数据read:惰性删除和定期删除
- 惰性删除:如果读取命令数据发现data过期,del命令删除数据
- 定期删除:循环采取一定key采样发现key过期 del
- 从节点故障
-
规避全量复制
- 低峰期建立第一次复制
- 使用故障转移功能,避免节点运行ID不匹配
- 设置合理的复制积压缓冲区大小
-
规避复制风暴
- 减少主节点(master)挂载从节点(slave)的数量
- 采用树状复制结构
- 主节点尽量分散在多台机器上,避免在单台机器上部署过多的主节点
-
主从配置不一致
-
示例拓扑:
redis-A redis-E | | redis-B redis-C redis-D
-
3、哨兵模式
3.1简介
1. 简介
哨兵核心功能:能够后台监控redis主机是否故障,如果故障了根据投票数自动将从库转换为主库
2. 原理
- 分布式架构,若干个sentinel节点,每个哨兵对其余哨兵和redis监控,节点不可达则进行下线表示。
- 主节点down则与其他哨兵协商,大多数认为master down,选出一个哨兵完成failover
3. 功能
- 监控:Redis和哨兵定期检测 alive
- 通知:哨兵将failover节点通知应用方
- failover:从-->主
- 配置提供:client从哨兵获取redis拓扑
3.2 搭建
(1) 安装部署
主:192.168.9.78:6410
从:192.168.9.78:6411
sentinel:26410 26411 26412
1. redis搭建主机
[root@OPS-9-78 ~]# redis-cli -p 6411 slaveof 127.0.0.1 6410
OK
[root@OPS-9-78 ~]# redis-cli -p 6411 info Replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6410
2. 安装sentinel
(1)目录初始化
mkdir -p /data/redis/{sentinel_26410,sentinel_26411,sentinel_26412}
cp -r /usr/local/redis/bin/ /data/redis/sentinel_26410/
cp -r /usr/local/redis/bin/ /data/redis/sentinel_26411/
cp -r /usr/local/redis/bin/ /data/redis/sentinel_26412/
(2)生成配置文件 sentinel.conf
-- 新建sentinel.conf 文件,名字绝不能错
26410节点
cat > /data/redis/sentinel_26410/redis_26410.conf <<EOF
port 26410
daemonize yes
pidfile /data/redis/sentinel_26410/redis-sentinel26410.pid
logfile "/data/redis/sentinel_26410/sentinel26410.log"
dir /data/redis/sentinel_26410/
sentinel monitor mymaster 127.0.0.1 6410 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
EOF
26411节点
cat > /data/redis/sentinel_26411/redis_26411.conf <<EOF
port 26411
daemonize yes
pidfile /data/redis/sentinel_26411/redis-sentinel26411.pid
logfile "/data/redis/sentinel_26411/sentinel26411.log"
dir /data/redis/sentinel_26411/
sentinel monitor mymaster 127.0.0.1 6410 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
EOF
26412节点
cat > /data/redis/sentinel_26412/redis_26412.conf <<EOF
port 26412
daemonize yes
pidfile /data/redis/sentinel_26412/redis-sentinel26412.pid
logfile "/data/redis/sentinel_26412/sentinel26412.log"
dir /data/redis/sentinel_26412/
sentinel monitor mymaster 127.0.0.1 6410 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
EOF
(3)启动哨兵
方式1:
/data/redis/sentinel_26410/bin/redis-server /data/redis/sentinel_26410/redis_26410.conf --sentinel
/data/redis/sentinel_26411/bin/redis-server /data/redis/sentinel_26411/redis_26411.conf --sentinel
/data/redis/sentinel_26412/bin/redis-server /data/redis/sentinel_26412/redis_26412.conf --sentinel
方式2:
/data/redis/sentinel_26410/bin/redis-sentinel /data/redis/sentinel_26410/redis_26410.conf
/data/redis/sentinel_26411/bin/redis-sentinel /data/redis/sentinel_26411/redis_26411.conf
/data/redis/sentinel_26412/bin/redis-sentinel /data/redis/sentinel_26412/redis_26412.conf
3、确认配置文件
$ cat /data/redis/sentinel_26410/redis_26410.conf
sentinel known-replica mymaster 127.0.0.1 6411
sentinel known-sentinel mymaster 127.0.0.1 26412 b8ac0a4d4b94e9bcb1fba566dfe64e8f82687fa9
sentinel known-sentinel mymaster 127.0.0.1 26411 0906d5f48c08a8d1e8ca19a6ef3ba893813d9246
4、确认info信息
[root@OPS-9-78 redis]# redis-cli -p 26410 info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:6410,slaves=1,sentinels=3
[root@OPS-9-78 redis]# redis-cli -p 26410 sentinel masters
1) 1) "name"
2) "mymaster"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "6410"
7) "runid"
8) "aa7828c0cbdf62432ad3b4174bdaaa6dd2c9ba4c"
9) "flags"
10) "master"
(2) 哨兵节点相关的配置
#动态设置参数 sentinel set
- 仅对哨兵结果有效
- 立马刷新配置文件,redis需要
config rewrite - 哨兵尽量配置一致
#相关配置说明
# sentinel 监控的主节点信息
sentinel monitor <master-name> <ip> <port> <quorum>
# sentinel判定节点不可达的时间,单位毫秒
sentinel down-after-milliseconds <master-name> <times>
动态设置: sentinel set <master-name> down-after-milliseconds 30000
#故障转移超时时间
sentinel failover-timeout <master-name> <times>
动态设置: sentinel set <master-name> failover-timeout 3600
#Sentinel监控的主节点配置了密码
sentinel auth-pass <master-name> <password>
动态设置: sentinel set <master-name> auth-pass password
#每次向新的主节点发起复制操作的从节点个数
sentinel parallel-syncs <master-name> <nums>
动态设置: sentinel set <master-name> parallel-syncs 2
(3) sentinel部署技巧
-
sentinel部署分配的原则
- 不应该部署在一台物理"机器"上
- 部署至少三个且奇数个的sentinel节点
- 按需选择 sentinel集群与redis集群的管控方式:1套sentinel-1套redis or 1套sentinel-多套redis
-
sentinel监控多个主节点
配置多个sentinel monitor
sentinel monitor master-test-1 192.168.9.78 6379 2 sentinel monitor master-test-2 192.168.9.78 6380 2
3.3 运维管理
sentinel API
-
sentinel masters#展示所有被监控的主节点状态以及相关的统计信息127.0.0.1:26410> sentinel masters 1) 1) "name" 2) "mymaster" 3) "ip" 4) "127.0.0.1" 5) "port" 6) "6410" 7) "runid" 8) "aa7828c0cbdf62432ad3b4174bdaaaddd2c9ba4c" 9) "flags" 10) "master" -
sentinel master <master name>#展示 的主节点状态以及相关的统计信息127.0.0.1:26410> sentinel master mymaster -
sentinel slaves <master name>#展示指定的从节点状态以及相关的统计信息127.0.0.1:26410> sentinel slaves mymaster 1) 1) "name" 2) "127.0.0.1:6411" 3) "ip" 4) "127.0.0.1" 5) "port" 6) "6411" 7) "runid" 8) "2fb124c588960cb07a04322af53068906367f114" 9) "flags" 10) "slave" -
sentinel sentinels <master name>#展示指定的Sentinel节点集合(不包含当前Sentinel节点) -
sentinel get-master-addr-by-name <master name>#返回主节点的IP地址和端口127.0.0.1:26410> sentinel get-master-addr-by-name mymaster 1) "127.0.0.1" 2) "6410" -
sentinel reset <pattern>#对符合条件的主节点的配置进行重置 -
sentinel failover <master name>#对指定主节点进行强制故障转移(没有和其他Sentinel节点"协商") -
sentinel ckquorum <master name>#检测当前可达的Sentinel节点总数是否达到的个数。/*例如 quorum=3,而当前可达的Sentinel节点个数为2个,那么将无法进行故障转移,Redis Sentinel的高可用特性也将失去*/ -
sentinel flushconfig#将Sentinel节点的配置强制刷到磁盘上 -
sentinel remove <master name>#取消当前Sentinel节点对于指定主节点的监控 -
sentinel monitor <master name> <ip> <port> <quorum> -
sentinel set <master name>#动态修改Sentinel节点配置选项 -
sentinel is-master-down-by-addr
业务连接
- 拿到Sentinel节点集合、masterName参数
- 客户端链接
- 遍历Sentinel节点集合获取一个可用的sentinel
- 通过
sentinel get-master-addr-by-name master-name这个API来获取对应主节点的相关信息 - 验证当前获取的"主节点"是真正的主节点
- 保持和Sentinel节点集合的联系,时刻获取关于主节点的相关信息
#python链接redis sentinel示例
python
from redis.sentinel import Sentinel
sentinel_list = [
("192.168.9.78", "26410"),
("192.168.9.78", "26411"),
("192.168.9.78", "26412")
]
mySentinel = Sentinel(sentinel_list)
master = mySentinel.master_for("mymaster", db=0)
slave = mySentinel.slave_for("mymaster", db=0)
# 使用master进行写的操作,使用slave进行读的操作
master.hset("key_name", "filed", "value")
slave.hget("key_name", "filed")
slave.hgetall("key_name")
3.4 哨兵实现原理
- 三个定时任务
- 主观下线/客观下线
- Sentinel领导者选举
- 故障转移
(1) 三个定时任务
- 10s一次,发送info获取redis拓扑
- 2s一次,发送哨兵订阅节点,保证新加入哨兵保存信息和消息交换
- 1s一次,哨兵向所有node(哨兵和redis)发送ping,确认心跳
(2) 主观下线和客观下线
- 主观下线
每隔1s对主/从节点/sentinel检测,超过down时间,哨兵判定主观下线
流程示意图:
# Sentinel-1
|
+-- 参数1 秒执行一次 ping
|
+-- 创建down-after-milliseconds无有效回复(sdown)
|
+-- 每隔1秒执行一次ping
# Sentinel-2
|
+-- 参数1 秒执行一次ping
|
+-- 创建down-after-milliseconds无有效回复(sdown)
|
+-- 每隔1秒执行一次ping
# Sentinel-3
|
+-- 参数1 秒执行一次ping
|
+-- 创建down-after-milliseconds无有效回复(sdown)
|
+-- 每隔1秒执行一次ping
主观下线发现是master,则跟其他哨兵进行投票。投票通过,则做出客观下线规定。选择一个哨兵(Raft协议选择负责failover的哨兵),进行failover。
(3) 故障转移
- 选出主节点
- 哨兵leader对新主
slaveof no one - 其余从节点-->新主
- 整理拓扑关系
选举流程:
从节点列表
|
+-- 过滤 slave-priority 最大节点
|
+-- yes -> 选择完毕
|
+-- no -> 继续选择复制偏移量 最大节点
|
+-- yes -> 选择完毕
|
+-- no -> 选择 runid 最小的节点
3.5 主从切换测试
(1) 手动进行故障转移
-
查看redis主从拓扑
[root@OPS-9-78 redis]# redis-cli -p 26410 info Sentinel master0:name=mymaster,status=ok,address=127.0.0.1:6410,slaves=1,sentinels=3 -
关闭6410
redis-cli -p 6410 shutdownsentinel的日志信息如下:
108297:X 06 Jun 2023 22:25:57.376 # +sdown master mymaster 127.0.0.1 6410 108297:X 06 Jun 2023 22:25:57.447 # +new-epoch 1 108297:X 06 Jun 2023 22:25:57.448 # +vote-for-leader 090645f48c08a8d1eac19a6ef3ba893813d9246 1 108297:X 06 Jun 2023 22:25:57.448 # +odown master mymaster 127.0.0.1 6410 #quorum 2/2 108297:X 06 Jun 2023 22:25:57.448 # Next failover delay: I will not start a failover before Tue Jun 6 22:31:58 2023 108297:X 06 Jun 2023 22:25:58.687 # +config-update-from sentinel 090645f48c08a8d1eac19a6ef3ba893813d9246 127.0.0.1 26411 @ mymaster 127.0.0.1 6410 108297:X 06 Jun 2023 22:25:58.687 # +switch-master mymaster 127.0.0.1 6410 127.0.0.1 6411 108297:X 06 Jun 2023 22:25:58.687 * +slave slave 127.0.0.1:6410 127.0.0.1 6410 @ mymaster 127.0.0.1 6411 -
故障转移的步骤
1)主库宕机
2)哨兵从库选择一个为主,规则依次按照
- 优先级高:slave-priority 100,值越小优先级越高
- 偏移量最大:原主机数据最全的
- runid最小:每个redis实例启动后都会随机生成一个40位的runid
3)哨兵向服务器发送slaveof 新主,复制新master
4)主服务器恢复加入变从
-
验证
[root@OPS-9-78 redis]# redis-cli -p 26410 info Sentinel master0:name=mymaster,status=ok,address=127.0.0.1:6411,slaves=1,sentinels=3 -
启动6410节点
/data/redis/6410/bin/redis-server /data/redis/6410/redis_6410.conf -
验证6410节点的状态--已自动变为6411的从节点
[root@OPS-9-78 sentinel_26410]# redis-cli -p 6410 info replication # Replication role:slave master_host:127.0.0.1 master_port:6411
(2) 强制主从切换
-
查看redis主从拓扑(主:6411,从:6410)
[root@OPS-9-78 sentinel_26410]# redis-cli -p 26410 info Sentinel master0:name=mymaster,status=ok,address=127.0.0.1:6411,slaves=1,sentinels=3 -
强制主从切换
redis-cli -p 26410 sentinel failover mymaster #为mymaster的主节点进行强制故障转移sentinel日志信息如下:
108297:X 06 Jun 2023 22:42:39.425 # Executing user requested FAILOVER of 'mymaster' 108297:X 06 Jun 2023 22:42:39.425 # +new-epoch 2 108297:X 06 Jun 2023 22:42:39.425 # +try-failover master mymaster 127.0.0.1 6411 108297:X 06 Jun 2023 22:42:39.450 # +vote-for-leader 29066269266ee74cdbdd8c12041e98fac2535512 2 108297:X 06 Jun 2023 22:42:39.450 # +elected-leader master mymaster 127.0.0.1 6411 108297:X 06 Jun 2023 22:42:39.450 # +failover-state-select-slave master mymaster 127.0.0.1 6411 108297:X 06 Jun 2023 22:42:39.513 # +selected-slave slave 127.0.0.1:6410 127.0.0.1 6410 @ mymaster 127.0.0.1 6411 108297:X 06 Jun 2023 22:42:39.513 * +failover-state-send-slaveof-none slave 127.0.0.1:6410 127.0.0.1 6410 @ mymaster 127.0.0.1 6411 108297:X 06 Jun 2023 22:42:39.575 * +failover-state-wait-promotion slave 127.0.0.1:6410 127.0.0.1 6410 @ mymaster 127.0.0.1 6411 108297:X 06 Jun 2023 22:42:40.485 # +promoted-slave slave 127.0.0.1:6410 127.0.0.1 6410 @ mymaster 127.0.0.1 6411 108297:X 06 Jun 2023 22:42:40.485 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6411 108297:X 06 Jun 2023 22:42:40.556 # +failover-end master mymaster 127.0.0.1 6411 108297:X 06 Jun 2023 22:42:40.556 # +switch-master mymaster 127.0.0.1 6411 127.0.0.1 6410 108297:X 06 Jun 2023 22:42:40.556 * +slave slave 127.0.0.1:6411 127.0.0.1 6411 @ mymaster 127.0.0.1 6410 -
结果验证(主:6410,从:6411)
[root@OPS-9-78 sentinel_26410]# redis-cli -p 26410 info Sentinel master0:name=mymaster,status=ok,address=127.0.0.1:6410,slaves=1,sentinels=3
4. Redis Cluster
cluster模式
去中心化的集群方案,支持在线数据迁移、节点扩缩容,内置哨兵自动恢复
哨兵模式
只有主节点对外提供服务,支持并发不高,还需要额外部署至少3台哨兵节点辅助
Redis cluster 与 codis对比
| codis | redis cluster | |
|---|---|---|
| 集群模式 | 中心化 | 无中心化 |
| 使用方式 | 通过proxy访问 | 使用cluster客户端直接redis,内置路由规则 |
| 性能 | 有性能损耗 | 高 |
| 数据库数量 | 多个 | 1个 |
| pipeline | 支持 | 仅支持单节点Pipeline,不支持跨节点 |
| 在线水平扩容 | 支持 | 支持 |
| redis版本 | 仅支持3.2.8,不支持升级 | 支持升级 |
| 可维护性 | 组件较多,部署复杂 | 运维简单,官方持续维护 |
| 自动恢复 | 基于redis哨兵实现 | 内置哨兵逻辑,无需额外部署 |
4.1 Cluster简介
- 3.0版本正式推出,解决单机内存、并发、流量等瓶颈
- Redis Cluster的核心目标是实现高可用性和水平扩展性
- 一般的分布式方案设计层
- client层:分区逻辑可控,但是需要自己处理数据路由,高可用,故障转移问题
- 代理层:简化客户端分布式逻辑,但是加重架构部署复杂和性能损耗
- Server层:数据库自己实现分布式(redis-cluster, tidb, ob)
(1) 数据分布hash
数据分区方案
- 分布式数据库解决问题,把数据集按照分区规则划分到不同节点上
- 常见顺序分区和hash分区,Redis采用hash分区
- hash分区特点
- 离散度好
- 数据分布式与业务无关
- 无顺序访问
hash分区规则
- 哈希分区规则有以下几种:
- 节点取余分区
- 一致性hash分区
- 虚拟槽分区
节点取余分区
根据用户ID和节点数量N, hash(key) % N 方式计算出hash值,应用。
优点:实现简单,数据映射稳定
缺点:需要提前规划容量,节点扩缩容则需要重新计算迁移数据
一致性hash分区
实现思路:每个node分配一个token,范围在 0~2^32,构成hash环,根据key计算hash值,在顺指针找到第一个大于该hash值的token节点
优点:加入删除节点仅影响hash环相邻节点。
缺点:
- 加减节点影响hash环部分数据无法命中,需要手动或者忽略数据
- 不适合少量节点,影响太大
- 增减节点加一倍或者减一半能保证数据负载均衡
- 均衡性:也有人把它定义为平衡性,是指哈希的结果能够尽可能分布到所有的节点中去,这样可以有效的利用每个节点上的资源。
- 单调性:当节点数量变化时哈希的结果应尽可能的保护已分配的内容不会被重新分派到新的节点。
- 分散性和负载:这两个其实是差不多的意思,就是要求一致性哈希算法对 key 哈希应尽可能的避免重复。
虚拟槽分区
简介:
- 基于一致性hash优化
- 把所有数据映射到一个固定范围的整数集合,定义为slots,为迁移和管理的基本单位
- Redis 0~16383,五个节点每个大约3276 slot
(2) Redis数据分区
采用虚拟slot分区,计算共识 slot = CRC16(key) & 16384
每个节点负责维护一部分槽
Redis slot特点:
- 简化扩缩容难度
- 仅维护slot和节点及key的映射查询即可
- 不需要客户端或者代理服务
功能限制:
- key批量操作有限:mset or mget,以为不同slot的key在多个节点不支持
- 事务操作有限:多个节点涉及分布式事务
- 大key支持不好:一个大hash仅能分配到一个slot
- 集群仅使用db0
- 不支持级联复制
槽范围示例:
0-3276 node-1 keys
3277-6553 node-2 'CRC16(key)&16383'
6554-9830 node-3 'RedisCluster
9831-13107 node-4 slot-0 data
13108-16383 node-5 slot-2 data
...
为什么是16384个slot?
- 正常的心跳数据包携带节点的完整配置,它能以幂等方式来更新配置。如果采用 16384 个插槽,占空间 2KB (16384/8);如果采用 65536 个插槽,占空间 8KB (65536/8)。
- Redis Cluster 不太可能扩展到超过 1000 个主节点,太多可能导致网络拥堵。
- 槽位越小,节点少的情况下,压缩率高
4.2 Redis Cluster部署
(1) redis-cli部署
命令说明
redis-cli --cluster help
Cluster Manager Commands:
create host1:port1 ... hostN:portN #创建集群
--cluster-replicas <arg> #从节点个数
check host:port #检查集群
--cluster-search-multiple-owners #检查是否有槽同时被分配给了多个节点
info host:port #查看集群状态
fix host:port #修复集群
--cluster-search-multiple-owners #修复槽的重复分配问题
reshard host:port #指定集群的任意一节点进行迁移slot,重新分slots
--cluster-from <arg> #需要从哪些源节点上迁移slot,可从多个源节点完成迁移,以逗号隔开,传递的是节点的node id,还可以直接传递--from all,这样源节点就是集群的所有节点,不传递该参数的话,则会在迁移过程中提示用户输入
--cluster-to <arg> #slot需要迁移的目的节点的node id,目的节点只能填写一个,不传递该参数的话,则会在迁移过程中提示用户输入
--cluster-slots <arg> #需要迁移的slot数量,不传递该参数的话,则会在迁移过程中提示用户输入
--cluster-yes #指定迁移时的确认输入
--cluster-timeout <arg> #设置migrate命令的超时时间
--cluster-pipeline <arg> #定义cluster getkeysinslot命令一次取出的key数量,不传的话使用默认值为10
--cluster-replace #是否直接replace到目标节点
rebalance host:port #指定集群的任意一节点进行平衡集群节点slot数量
--cluster-weight <node1=w1...nodeN=wN> #指定集群节点的权重
--cluster-use-empty-masters #设置可以让没有分配slot的主节点参与,默认不允许
--cluster-timeout <arg> #设置migrate命令的超时时间
--cluster-simulate #模拟rebalance操作,不会真正执行迁移操作
--cluster-pipeline <arg> #定义cluster getkeysinslot命令一次取出的key数量,默认值为10
--cluster-threshold <arg> #迁移的slot阈值超过threshold,执行rebalance操作
--cluster-replace #是否直接replace到目标节点
add-node new_host:new_port existing_host:existing_port #添加节点,把新节点加入到指定的集群,默认添加主节点
--cluster-slave #新节点作为从节点,默认随机一个主节点
--cluster-master-id <arg> #给新节点指定主节点
del-node host:port node_id #删除给定的一个节点,成功后关闭该节点服务
call host:port command arg arg ... arg #在集群的所有节点执行相关命令
set-timeout host:port milliseconds #设置cluster-node-timeout
import host:port #将外部redis数据导入集群
--cluster-from <arg> #将指定实例的数据导入到集群
--cluster-copy #migrate时指定copy
--cluster-replace #migrate时指定replace
help
集群部署
端口分配:
6510 -> 6520
6511 -> 6521
6512 -> 6522
#启动6个redis节点
1. redis部署安装
(1)目录初始化
mkdir -p /data/redis/{6510,6511,6512,6520,6521,6522}
cp -r /usr/local/redis/bin/ /data/redis/6510/
cp -r /usr/local/redis/bin/ /data/redis/6511/
cp -r /usr/local/redis/bin/ /data/redis/6512/
cp -r /usr/local/redis/bin/ /data/redis/6520/
cp -r /usr/local/redis/bin/ /data/redis/6521/
cp -r /usr/local/redis/bin/ /data/redis/6522/
(2)生成配置文件
cat > /data/redis/6510/redis_6510.conf <<EOF
daemonize yes
timeout 0
databases 16
dir "/data/redis/6510"
slowlog-log-slower-than 10000
slowlog-max-len 128
hz 10
port 6510
maxmemory 100mb
appendonly yes
appendfsync everysec
appendfilename "appendonly-6510.aof"
dbfilename "dump-6510.rdb"
logfile "/data/redis/6510/redis_6510.log"
pidfile /data/redis/6510/redis_6510.pid
protected-mode no
requirepass ""
masterauth ""
cluster-enabled yes
cluster-config-file /data/redis/6510/node_6510.conf
cluster-node-timeout 15000
cluster-replica-validity-factor 10
cluster-migration-barrier 1
cluster-require-full-coverage no
EOF
cd /data/redis/6510/
cp redis_6510.conf /data/redis/6511/redis_6511.conf
cp redis_6510.conf /data/redis/6512/redis_6512.conf
cp redis_6510.conf /data/redis/6520/redis_6520.conf
cp redis_6510.conf /data/redis/6521/redis_6521.conf
cp redis_6510.conf /data/redis/6522/redis_6522.conf
sed -i 's/6510/6511/g' /data/redis/6511/redis_6511.conf
sed -i 's/6510/6512/g' /data/redis/6512/redis_6512.conf
sed -i 's/6510/6520/g' /data/redis/6520/redis_6520.conf
sed -i 's/6510/6521/g' /data/redis/6521/redis_6521.conf
sed -i 's/6510/6522/g' /data/redis/6522/redis_6522.conf
(3)启动
/data/redis/6510/bin/redis-server /data/redis/6510/redis_6510.conf
/data/redis/6511/bin/redis-server /data/redis/6511/redis_6511.conf
/data/redis/6512/bin/redis-server /data/redis/6512/redis_6512.conf
/data/redis/6520/bin/redis-server /data/redis/6520/redis_6520.conf
/data/redis/6521/bin/redis-server /data/redis/6521/redis_6521.conf
/data/redis/6522/bin/redis-server /data/redis/6522/redis_6522.conf
(4)查看
# ps -ef |grep cluster
root 33547 1 0 13:12 ? 00:00:00 /data/redis/6510/bin/redis-server *:6510 [cluster]
root 33808 1 0 13:14 ? 00:00:00 /data/redis/6511/bin/redis-server *:6511 [cluster]
root 33991 1 0 13:14 ? 00:00:00 /data/redis/6512/bin/redis-server *:6512 [cluster]
root 34102 1 0 13:15 ? 00:00:00 /data/redis/6520/bin/redis-server *:6520 [cluster]
root 34193 1 0 13:15 ? 00:00:00 /data/redis/6521/bin/redis-server *:6521 [cluster]
root 34246 1 0 13:16 ? 00:00:00 /data/redis/6522/bin/redis-server *:6522 [cluster]
2. cluster创建
#redis-trib.rb分装到redis-cli命令里面
redis-cli --cluster help
redis-cli --cluster create 192.168.9.78:6510 192.168.9.78:6511 192.168.9.78:6512 192.168.9.78:6520 192.168.9.78:6521 192.168.9.78:6522 --cluster-replicas 1 # cluster-replicas: 指定内部数,即从节点
[root@OPS-9-78 6520]# redis-cli --cluster create 192.168.9.78:6510 192.168.9.78:6511 192.168.9.78:6512 192.168.9.78:6520 192.168.9.78:6521 192.168.9.78:6522 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 192.168.9.78:6521 to 192.168.9.78:6510
Adding replica 192.168.9.78:6522 to 192.168.9.78:6511
Adding replica 192.168.9.78:6520 to 192.168.9.78:6512
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 69bc68c2ef584fcd3aadc645cdad2e31312Bbd4 192.168.9.78:6510
slots:[0-5460] (5461 slots) master
M: ed7c2f9b8118a7d8e1ale4bab9258d0eeae3ca09 192.168.9.78:6511
slots:[5461-10922] (5462 slots) master
M: 34493947ff587b203aa4649a311e616cb60c9052 192.168.9.78:6512
slots:[10923-16383] (5461 slots) master
S: f899f49487288251fcec21c738eb1e581ea05da4 192.168.9.78:6520
replicates ed7c2f9b8118a7d8e1ale4bab9258d0eeae3ca09
S: e11556f28feesdd71de45le18a728a2f5ba6f65b 192.168.9.78:6521
replicates 34493947ff587b203aa4649a311e616cb60c9052
S: e5663e0168079eb26064eldbfc720506066962e 192.168.9.78:6522
replicates 69bc68c2ef584fcd3aadc645cdad2e31312Bbd4
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
.
>>> Performing Cluster Check (using node 192.168.9.78:6510)
M: 69bc68c2ef584fcd3aadc645cdad2e31312bbd44 192.168.9.78:6510
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 34493947ff587b203aa4649a311e616cb60c9052 192.168.9.78:6512
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: e5663e0168079eb26064eldbfc720506066962e 192.168.9.78:6522
slots: (0 slots) slave
replicates 69bc68c2ef584fcd3aadc645cdad2e31312bbd44
S: e11556f28feesdd710e45le188728a275ba6f65b 192.168.9.78:6521
slots: (0 slots) slave
replicates 34493947ff587b203aa4649a311e616cb60c9052
S: f899f49487288251feec21c738eb1e58lea05da4 192.168.9.78:6520
slots: (0 slots) slave
replicates ed7c2698ll8a748elale4bab9258d0eeae3ca39
M: ed7c2698ll8a748elale4bab9258d0eeae3ca39 192.168.9.78:6511
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
3、集群状态确认
[root@OPS-9-78 6520]# redis-cli -C -p 6510
127.0.0.1:6510> cluster info #打印集群的信息
cluster_state:ok #ok状态表示集群可以正常接受查询请求
cluster_slots_assigned:16384 #已分配到集群节点的哈希槽数量
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6 #集群中节点数量
cluster_size:3 #至少包含一个slot且能够提供服务的master节点数量
集群节点扩容
新加入节点:主:6530,从:6531
#1、启动redis6530/6531两个实例
mkdir -p /data/redis/{6530,6531}
cp -r /usr/local/redis/bin/ /data/redis/6530/
cp -r /usr/local/redis/bin/ /data/redis/6531/
cd /data/redis/6510/
cp redis_6510.conf /data/redis/6530/redis_6530.conf
cp redis_6510.conf /data/redis/6531/redis_6531.conf
sed -i 's/6510/6530/g' /data/redis/6530/redis_6530.conf
sed -i 's/6510/6531/g' /data/redis/6531/redis_6531.conf
/data/redis/6530/bin/redis-server /data/redis/6530/redis_6530.conf
/data/redis/6531/bin/redis-server /data/redis/6531/redis_6531.conf
-- 启动完成后,6530 和 6531 两个节点还处于游离状态,还未加入redis集群中!
#2、加入主节点到集群,并进行slot分配
(1) 新增master节点 192.169.9.78:6530
$ redis-cli --cluster add-node 127.0.0.1:6530 127.0.0.1:6510 #6530表示要加入的节点,6510表示集群中任意一个节点,用来标识这个集群
(2) 集群重新sharding
$ redis-cli --cluster reshard 127.0.0.1:6530
How many slots do you want to move (from 1 to 16384)? 4095
What is the receiving node ID? a4cb8f370148cbc3f67ea5b69d839f6008904804 #6530的nodeid
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1: all
(3) 验证集群的slot分配
192.168.9.78:6510> cluster nodes
a4cb8f370148cbc3f67ea5b69d839f6008904804 127.0.0.1:6530@16530 master - 0 1686116935075 7 connected 0-1363 5461-6826 10923-12286
交互内容:
#第一次交互:需要迁移多少个槽
How many slots do you want to move (from 1 to 16384)?
#第二次交互:接受 slot 槽的节点ID
What is the receiving node ID?
#第三次交互:哪些节点需要导出
all 为自动分配,第二种方式为手动分配。
#第四次交互:确认信息
Do you want to proceed with the proposed reshard plan (yes/no)?
yes
# 3、从节点直接加入集群,指定master即可
新增slave节点 6511
$ redis-cli --cluster add-node 127.0.0.1:6531 127.0.0.1:6510 --cluster-slave --cluster-master-id a4cb8f370148cbc3f67ea5b69d839f6008904804
-- 8231为要加入的节点。cluster-slave代表作为slave加入,cluster-master-id 后面加master (6530)的node-id
# 4、reshard 脚本命令
redis-cli --cluster reshard 127.0.0.1:6510 --cluster-from (node_id) --cluster-to (接受 slot_id) --cluster-slots 2000 --cluster-yes
说明:
--cluster 后的host::port 指定集群的任意一节点进行迁移slot,重新分slots
--cluster-from: 表示slot目前所在的节点的node ID,多个ID用逗号分隔(node_id通过 clusters nodes 查看,第一列为nodeId)
--cluster-to: 表示需要新分配节点的node ID (每次只能分配一个)
--cluster-slots: 分配的 slot 数量
--cluster-yes: 指示群集管理器对命令的提示自动回答"是",允许它以非交互模式运行
集群节点缩容
#缩容节点
master节点:6530
slave节点:6531
#1. 清空master的slot
redis-cli --cluster reshard 127.0.0.1:6510 --cluster-from adcb8f370148cbc3f67ea5b69d839f6008904804 --cluster-to 34493942ff587b203a44649a311e61ecb60c9052 ed7c2f9b8118a7d8elale4bb9258d0eeae3cad9 69bc68c2ef584fcd3aadc645cdad2e313128bd44 --cluster-slots 4095 --cluster-yes
#2. 检查节点是否全部迁移走
192.168.9.78:6510> cluster nodes
adcb8f370148cbc3f67ea5b69d839f6008904804 127.0.0.1:6530@16530 master - 0 1686140461000 7 connected
#3. 删除从节点
redis-cli --cluster del-node 127.0.0.1:6510 caf7434be9150a3599d1d2e365ac0eab319b835c
>>> Removing node caf7434be9150a3599d1d2e365ac0eab319b835c from cluster 127.0.0.1:6510
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.
#4. 删除主节点
redis-cli --cluster del-node 127.0.0.1:6510 adcb8f370148cbc3f67ea5b69d839f6008904804
#5. 平衡节点
redis-cli --cluster rebalance 127.0.0.1:6510
说明:
- check or rebalance 后面跟的是集群中任意一个节点即可
- 当集群每个节点的 slot 槽为不均衡后,使用 rebalance 重新平衡。
- 使用后每个 master 的 slot 数量就会再次平衡,在 2% 阈值范围内,使用命令也不会平衡,会进行如下提示:
*** No rebalancing needed! All nodes are within the 2.00% threshold.
(2) cluster相关配置
cluster-enabled#redis集群开关cluster-config-file#redis集群配置文件,每个节点都有一个集群配置文件cluster-node-timeout#集群节点互联超时毫秒数,默认为15000毫秒cluster-slave-validity-factor#判断slave节点与master节点断线的时间是否过长cluster-migration-barrier#master节点需要的最小slave节点数,只有达到这个数,master节点失败时,slave节点才能迁移到其他master节点上cluster-require-full-coverage#设置为no,可以在slot没有全部分配的时候提供服务
4.3 Redis Cluster核心原理
(1) gossip通信流程
gossip简介
- 分布式存储维护节点meta info:那些节点负责那些数据,故障状态
- 常见元数据维护方式:集中式 or P2P
- redis取用p2p的gossip(流言)协议
- 节点彼此一直交换信息,一段时间后所有节点知道集群完整信息
- redis过程说明
- 集群每个节点单独开辟一个TCP通道,用于节点间通信,redis端口+10000
- 节点在固定周期内根据特定规则发送ping消息
- 收到ping的节点恢复pong消息
- 随着节点加入、故障、slot变化,不断通过ping/pong消息通信,一段时间后所有node知道cluster的最新状态,集群信息同步
gossip消息类型
- redis常用的gossip消息:ping, pong, meet, fail
meet: 新节点加入ping: 集群交换最频繁信息,监测在线和集群信息状态,封装自己和其他节点部分状态数据pong: 接受ping meet 作为消息回应,同时封装了自身的数据fail: 判定集群节点下线的fail消息,其他node收到后更新下线状态
ping和meet的消息接受流程
接收 ping/meet 消息
|
+-- 消息解析
|
+-- 消息头
|
+-- 消息体
|
+-- 发送 meet 消息进行节点握手
|
+-- 是否新节点
|
+-- 更新该节点状态到本地列表
|
+-- 回复 pong 消息
消息解析流程
-
节点通信选择-兼容实时和成本
- 通信是个定时任务
- 每秒10次,1秒1次
- 发送节点
- 5个最久没通信节点
- 通信时间大于 node-timeout/2
节点定时任务
|
+-- 每秒执行 10 次间隔 1 秒
|
+-- 选择发送节点 ping 消息数据
|
+-- 每秒随机 5 次找出最久没通信节点
|
+-- 最后通信时间大于 node-timeout/2
|
+-- 节点自身信息
|
+-- 发送 ping 消息
|
+-- 1/10 其他节点信息
(2) 请求路由
● MOVED 重定向
概念:在集群模式下,Redis接收任何键相关命令时首先计算键对应的槽,再根据槽找出所对应的节点,如果节点是自身,则处理键命令;否则回复MOVED重定向错误,通知客户端请求正确的节点。
示例:
GET x
-MOVED 3999 127.0.0.1:6381
#查看key的slot
cluster keyslot key:test:1
请求重定向注意事项
- 网络延迟和性能会影响请求重定向的效率,需要保证集群节点之间的网络连接稳定可靠。
- 请求重定向会增加集群节点的负载,可能会导致客户端的性能下降,因此需要在开发中进行评估和优化。
- 集群节点的扩容和缩容也会影响请求重定向的效率和正确性,需要注意集群拓扑的变化对请求重定向的影响。
moved重定向执行流程
1) 发送键命令
|
+-- 信息节点
|
+-- 2) 计算槽和对应节点
|
+-- 是否指向自身
|
+-- yes -> 3-2) 执行命令
|
+-- no -> 3-1) 回复 MOVED
● ASK重定向
场景:Cluster迁移时使用
- 一个slot迁移会很难节点的该槽位被设置为migrating状态,B节点被设置为importing的槽位(CLUSTER SETSLOT命令)
- 获得节点的key列表->逐个key迁移(A节点用dump命令序列化->B节点restore反序列化)
- 对应的key从A节点删除
在迁移A->B时客户端节点请求:
- key还未迁移返回结果
- 键值迁移-ASK targetNodeAddr重定向到B
- B当成自己key处理,如果有则处理命令
ASK与MOVED的本质区别:
- ASK重定向说明集群正在进行slot数据迁移,客户端无法知道什么时候迁移完成,因此只能是临时性的重定向,客户端不会更新slot缓存
- MOVED重定向说明链对应的槽已经明确指定到新的节点,因此需要更新slot缓存
ASK重定向流程示意图:
# 客户端
|
+-- source
| |
| +-- slot 迁移中 ...
| |
| +-- 待迁移键
| | |
| | +-- key-value
| | |
| | +-- key-value
| |
| +-- 已迁移键
| |
| +-- key-value
| |
| +-- key-value
|
+-- target
|
+-- 1) 发送键命令
| |
| +-- client
| |
| +-- 2) 回复 ASK 转向
|
+-- 3) asking
|
+-- 4) 发送键命令
|
+-- 5) 响应结果
(3) 故障发现
故障发现(通过ping/pong节点)主要环节:
- 主观下线(pfail)
- 客观下线(fail)
主观下线
主观:某个node认为一个node不可用
客观:多数节点达成节点一致不可用,进行故障转移
主观下线识别流程:
1.2) 更新节点b,最后通信时间
|
+-- 节点A
|
+-- cron定时任务
|
+-- 发送ping消息
|
+-- 3) 与节点b最后通信时间超过cluster-node-timeout标记pfail状态
|
+-- 是否成功
|
+-- yes -> 节点B
|
+-- no -> 12) 通信异常断开连接
客观下线
#主节点才有权利下线,半数以上对网络分区场景
1. 节点信息gossip给其他节点,接受节点发现pfail下线消息
2. 更新自己的clusternode消息结构,广播
3. 其他节点进行更新判定 达到多数则fail下线
4. 集群广播fail,通知所有节点客观下线 node id
5. 触发从节点故障转移流程
客观下线逻辑流程:
接受 ping 消息
|
+-- 消息解析
|
+-- 包含其他 pfail 节点 主节点发送消息
|
+-- 维护故障链表
|
+-- 尝试客观下线
尝试客观下线流程:
尝试客观下线
|
+-- 计算有效下线报告数量
|
+-- 是否大于带节点总数一半
|
+-- yes -> 更新为客观下线
| |
| +-- 向集群广播下线节点的 fail 消息
|
+-- no -> 退出
(4) 故障转移
slave发现复制的master进入fail时,则会触发故障恢复流程:
-
资格检查
判断与master断线时间,是否是新节点具备资格选举,看我与主节点连接时间是否很新
cluster-node-time*cluster-slave-validity-factor 则slave不具备故障转移资格
-
准备选举时间
slave根据自身复制偏移量设置延迟选举时间,复制偏移量最大的slave将提前触发故障选举流程
-
发起选举
-
更新配置纪元(集群最大版本),应用场景:新节点接入、slot冲突、从节点投票选举冲突检测
-
广播选举消息
127.0.0.1:6379> cluster info cluster_current_epoch:15 # 整个集群最大配置纪元 cluster_my_epoch:13 # 当前主节点配置纪元
-
-
投票选举
- 主节点进行投票
- 大于半数以上,选举成功
-
替换主节点
- slave取消复制变为主节点
- 执行clusteraddslot把slot委派给自己
- 广播pong消息
-
故障节点启动
- 发现自己slot没有,则成为新主节点的从节点
故障转移的时间:
failover-time(毫秒) ≤ cluster-node-timeout + cluster-node-timeout/2 + 1000(毫秒)
故障恢复流程示意图:
故障恢复流程
|
+-- 1)资格检查
|
+-- 2)准备选择时间
|
+-- 3)发起选择
|
+-- 4)选择投票
|
+-- 5)替换主节点
5个主节点
|
+-- master a
|
+-- master b
|
+-- 投票
| |
| +-- slave b-2 收集 3 张选票,大于 N/2+1
|
+-- 投票
|
+-- slave b-1 可替换主节点
故障恢复流程 从节点slave b-1成功获得3张选票
4.4 Redis Cluster运维管理
(1) 操作命令管理
cluster info
192.168.225.130:8200> cluster info
cluster_state:ok //fail, 如果节点能够接收查询,为ok;如果一个slot未绑定,处于错误状态,则为fail
cluster_slots_assigned:16384 //要使节点正常工作,这个数字应该是16384,这意味着每个哈希槽应该映射到一个节点。
cluster_slots_ok:16384 // 映射到未处于fail 或PPAIL状态的节点的哈希槽数。
cluster_slots_pfail:0 // 处于PPAIL状态的节点的哈希槽数。注意,只要故障检测算法没有将PPAIL状态提升为PAIL,这些哈希槽仍然可以正常工作。PPAIL只意味着我们目前不能与节点通信,但可能只是一个暂时的错误。
cluster_slots_fail:0 // 处于fail节点的哈希槽数,假如该数值不为0,则该节点不能提供查询。除非配置中将cluster-require-full-coverage 设置为 no
cluster_known_nodes:6 // 集群中节点的总数
cluster_size:3 // 集群中master节点的数量
cluster_current_epoch:6 // 本地当前epoch变量。用于在故障转移期间创建唯一的递增版本号。
cluster_my_epoch:1 // 我们正在讨论的节点的配置单元。这是分配给该节点的当前配置版本。
cluster_stats_messages_ping_sent:1298
cluster_stats_messages_pong_sent:832
cluster_stats_messages_meet_sent:2130 // 集群node-to-node二进制总线发送的消息数量
cluster_stats_messages_ping_received:832
cluster_stats_messages_pong_received:849
cluster_stats_messages_meet_received:1681 // 集群node-to-node二进制总线接收的消息数量
cluster nodes
查看所有节点的状态信息
192.168.9.78:6510> cluster nodes
69bc68c2ef584fcd3aadc645cdad2e313128bd44 127.0.0.1:6510@16510 master - 0 1686137124000 1 connected 1364-5460
e56e6e0168079eb26064e1d1bfc720506066962e 127.0.0.1:6522@16522 slave 69bc68c2ef584fcd3aadc645cdad2e313128bd44 0 1686137123000 1 connected
cluster slots
192.168.225.130:8200> cluster slots
192.168.9.78:6510> cluster slots
1) 1) (integer) 0 //开始slot位置
2) (integer) 1363 //结束slot位置
3) 1) "127.0.0.1" //master IP
2) (integer) 6530 //master port
3) "adeb87370148abc3f67ea5b6da39f6008904804" //master node_id
4) 1) "127.0.0.1" //slave IP
2) (integer) 6531 //slave port
3) "caf7434be9150a359g4d2e365ac0eab319b835c" //slave node_id
cluster addslots
(该命令执行的时候需要在需要addslot的master上执行,才会生效,其他节点执行出现ok,但是实际是不成功的。只有当所有指定的slot都已与某个节点关联时,该命令才有效)
> cluster addslots 10000
addslots多个:
for ((i=10923;i<=16383;i++)); do /data/redis/bin/redis-cli -h 192.168.9.78 -P 6383 CLUSTER ADDSLOTS $i ; done
cluster delslots
该命令执行的时候需要在10000这个slot所在的master上执行,才会生效,其他节点执行出现ok,但是实际是不成功的。只有当所有指定的插槽都已与某个节点关联时,该命令才有效
> cluster delslots 10000
cluster failover
手动failover支持三种模式的failover:
- 缺省(manual)
- force
- takeover
#manual
说明:该命令只能在群集slave节点执行,让slave节点进行一次人工故障切换。
流程如下:
(1) 当前slave节点告知其master节点停止处理来自客户端请求
(2) master 节点将当前replication offset 回复给该slave节点
(3) 该slave节点在未应用replication offset之前不做任何操作,以保证master传来的数据均被处理。
(4) 该slave 节点进行故障转移,从群集中大多数的master节点获取epoch,然后广播自己的最新配置
(5) 原master节点收到配置更新:解除客户端访问的阻塞,回复重定向信息,以便客户端可以和新master通信。
当该slave节点(将切换为新master节点)处理完来自master的所有复制,客户端访问将会自动由原master节点切换至新master节点
#FORCE
说明:master节点down的情况下的人工故障转移
FORCE 选项:slave节点不和master协商(master也许已不可达),从上如4点开始进行故障切换。当master已不可用,而我们想要做人工故障转移时,该选项很有用。
#TAKEOVER
说明:忽略群集一致验证的的人工故障切换,无群集共识的手动故障转移
举例:群集中主节点和从节点在不同的数据中心,当所有主节点down掉或被网络分区隔离,需要用该参数将slave节点批量切换为master节点。
流程如下:
(1) 独自生成新的configEpoch,若本地配置epoch非最大的,则取当前有效epoch值中的最大值并自增作为新的配置epoch
(2) 将原master节点管理的所有哈希槽分配给自己,同时尽快分发最新的配置给所有当前可达节点,以及后续恢复的故障节点,期望最终配置分发至所有节点
cluster meet
cluster forget
192.168.225.130:8200> cluster forget 31baabca10f43d06737a17a121b09148fa82d361
OK
集群一次性forget某个node
ip_list=("172.21.100.100:6381" "172.21.100.101:6382" "172.21.100.102:6382")
for ip in "${ip_list[@]}"; do redis-cli -h "${ip%:*}" -p "${ip#*:}" -a "密码" CLUSTER FORGET cald5e20e7183bfda7d872bab607c352d65f53ca; done
其他命令
#cluster slaves node-id //获取指定主节点的所有从节点信息,node_id为master的node_id
#cluster replicas node-id //(node_id为master的node_id)
说明:让一个节点成为从节点。其中命令执行必须在对应的从节点上执行,nodeId是要复制主节点的节点ID
#cluster keyslot <keyname>
说明:返回的是一个整数,aaa这个key所在的slot,一个slot里面肯定会有很多个key
127.0.0.1:6530> cluster keyslot aaa
(integer) 10439
#cluster count-failure-reports node-id
说明:该命令返回指定节点的失败报告数量。
192.168.225.130:8200> cluster count-failure-reports d455781dd9979f2a1b9d45f7d8ae66d818989c3
(integer) 0
#cluster countkeysinslot ,slot里面key的数量
127.0.0.1:6511> cluster countkeysinslot 10439
(integer) 1
#cluster getkeysinslot
127.0.0.1:6511> cluster getkeysinslot 10439 10 #说明:10439为slot,10,表示:返回该槽里面的10个key。返回结果为数组
1) "aaa"
(2) 人工故障转移测试
人工故障切换(2节点正常)
主:6510,
从:6522
127.0.0.1:6522> cluster failover
ok
#状态检查
127.0.0.1:6522> info Replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.9.78,port=6510,state=online,offset=37086,lag=0
人工故障转移(master down)
主:6522,
从:6510
127.0.0.1:6510> cluster failover FORCE
ok
#状态检查
127.0.0.1:6510> info Replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.9.78,port=6522,state=online,offset=37086,lag=0
Force选项:
slave节点不和master协商(master也许已不可达),从上如4步开始进行故障切换。当master已不可用,而我们想要做人工故障转移时,该选项很有用。
但是,即使使用FORCE选项,我们依然需要集中大多数master节点有效,以便对这次切换进行验证,同时为将成为新master的slave节点生成新的配置epoch
人工故障转移(忽略一致性验证)
主:6510,
从:6522
127.0.0.1:6522> cluster failover TAKEOVER
ok
#状态检查
127.0.0.1:6522> info Replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.9.78,port=6510,state=online,offset=38052,lag=1