redis集群搭建
Redis入门简介与主从搭建
前言
Redis
是一种基于 键值对 的 NoSQL
数据库。与很多键值对数据库不同,Redis
提供了丰富的 值数据存储结构,包括 string
(字符串)、hash
(哈希)、list
(列表)、set
(集合)、zset
(有序集合)、bitmap
(位图)等等。
正文
Redis
是一个使用 ANSI C
编写的开源、支持 网络、基于 内存、单线程模型、可选持久性 的 键值对存储数据库。
1. Redis的特性
-
速度快,最快可达到
10w QPS
(基于 内存,C
语言,单线程 架构); -
基于 键值对 (
key/value
) 的数据结构服务器。全称Remote Dictionary Server
。包括string
(字符串)、hash
(哈希)、list
(列表)、set
(集合)、zset
(有序集合)、bitmap
(位图)。同时在 字符串 的基础上演变出 位图(BitMaps
)和HyperLogLog
两种数据结构。3.2
版本中加入GEO
(地理信息位置)。 -
丰富的功能。例如:键过期(缓存),发布订阅(消息队列),
Lua
脚本(自己实现Redis
命令),事务,流水线(Pipeline
,用于减少网络开销)。 -
简单稳定。无外部库依赖,单线程模型。
-
客户端语言多。
-
持久化(支持两种 持久化 方式
RDB
和AOF
)。 -
主从复制(分布式的基础)。
-
高可用(
Redis Sentinel
),分布式(Redis Cluster
)和 水平扩容。
2. Redis的应用场景
2.1. 缓存
合理的使用 缓存 能够明显加快访问的速度,同时降低数据源的压力。这也是 Redis
最常用的功能。Redis
提供了 键值过期时间(EXPIRE key seconds
)设置,并且也提供了灵活控制 最大内存 和 内存溢出 后的 淘汰策略。
2.2. 排行榜
每个网站都有自己的排行榜,例如按照 热度排名 的排行榜,发布时间 的排行榜,答题排行榜 等等。Redis
提供了 列表(list
)和 有序集合(zset
)数据结构,合理的使用这些数据结构,可以很方便的构建各种排行榜系统。
2.3. 计数器
计数器 在网站应用中非常重要。例如:点赞数加 1
,浏览数 加 1
。还有常用的 限流操作,限制每个用户每秒 访问系统的次数 等等。Redis
支持 计数功能(INCR key
),而且计数的 性能 也非常好,计数的同时也可以设置 超时时间,这样就可以 实现限流。
2.4. 社交网络
赞/踩,粉丝,共同好友/喜好,推送,下拉刷新等是社交网站必备的功能。由于社交网站 访问量通常比较大,而且 传统的数据库 不太适合保存这类数据,Redis
提供的 数据结构 可以相对比较容易实现这些功能。
2.5. 消息队列
Redis
提供的 发布订阅(PUB/SUB
)和 阻塞队列 的功能,虽然和专业的消息队列比,还 不够强大,但对于一般的消息队列功能基本满足。
3. Redis的安装配置
下面介绍一下 Redis
的安装流程。我会按照如下的顺序,逐步搭建出 高可用 的 Redis
缓存服务器集群。
-
Redis
单机服务器 搭建 -
Redis
主从复制 搭建 -
Redis-Sentinel
高可用 搭建
3.1. Redis单机服务器安装
3.1.1. 下载并解压
首先从 Redis
官网下载 Redis
源代码并解压,这里使用的是 最新稳定版本 4.0.11
。依次执行如下命令:
cd /usr/local/
wget http://download.redis.io/releases/redis-4.0.11.tar.gz
tar -zxvf redis-4.0.2.tar.gz
复制代码
3.1.2. 编译并安装
下载并解压完毕后,则对 源码包 进行 编译安装,这里 Redis
安装路径为 /usr/local/redis
。
注意:
make install PREFIX
=目标安装路径
cd /usr/local/redis-4.0.11
make install PREFIX=/usr/local/redis
复制代码
安装完成时,/usr/local/redis/bin
目录下会生成的几个可执行的文件。
redis-server | 启动 redis 服务 |
redis-cli redis | 命令行客户端 |
redis-benchmark | redis 基准测试工具 |
redis-check-aof | redis AOF 持久化文件检测和修复工具 |
redis-check-dump | redis RDB 持久化文件检测和修复工具 |
redis-sentinel | 启动 redis sentinel |
复制 Redis
相关命令到 /usr/local/bin
目录下,这样就可以直接执行这些命令,不用写全路径。
$ cd /usr/local/redis/bin/
$ sudo sudo cp redis-cli redis-server redis-sentinel /usr/local/bin
复制代码
3.1.3. 修改Redis配置文件
安装完成之后将 Redis
配置文件拷贝到 /usr/local
下,redis.conf
是 Redis
的配置文件,redis.conf
在 Redis
源码目录,port
默认是 6379
。
$ sudo cp /usr/local/redis-4.0.11/redis.conf /usr/local/
复制代码
Redis
配置文件主要参数解析参考:
# redis进程是否以守护进程的方式运行,yes为是,no为否(不以守护进程的方式运行会占用一个终端)。
daemonize no
# 指定redis进程的PID文件存放位置
pidfile /var/run/redis.pid
# redis进程的端口号
port 6379
# 绑定的主机地址
bind 127.0.0.1
# 客户端闲置多长时间后关闭连接,默认此参数为0即关闭此功能
timeout 300
# redis日志级别,可用的级别有debug.verbose.notice.warning
loglevel verbose
# log文件输出位置,如果进程以守护进程的方式运行,此处又将输出文件设置为stdout的话,就会将日志信息输出到/dev/null里面去了
logfile stdout
# 设置数据库的数量,默认为0可以使用select <dbid>命令在连接上指定数据库id
databases 16
# 指定在多少时间内刷新次数达到多少的时候会将数据同步到数据文件
save <seconds> <changes>
# 指定存储至本地数据库时是否压缩文件,默认为yes即启用存储
rdbcompression yes
# 指定本地数据库文件名
dbfilename dump.db
# 指定本地数据问就按存放位置
dir ./
# 指定当本机为slave服务时,设置master服务的IP地址及端口,在redis启动的时候他会自动跟master进行数据同步
slaveof <masterip> <masterport>
# 当master设置了密码保护时,slave服务连接master的密码
masterauth <master-password>
# 设置redis连接密码,如果配置了连接密码,客户端在连接redis是需要通过AUTH<password>命令提供密码,默认关闭
requirepass footbared
# 设置同一时间最大客户连接数,默认无限制。redis可以同时连接的客户端数为redis程序可以打开的最大文件描述符,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回 max number of clients reached 错误信息
maxclients 128
# 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key。当此方法处理后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区
maxmemory<bytes>
# 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no。
appendonly no
# 指定跟新日志文件名默认为appendonly.aof
appendfilename appendonly.aof
# 指定更新日志的条件,有三个可选参数 - no:表示等操作系统进行数据缓存同步到磁盘(快),always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全), everysec:表示每秒同步一次(折衷,默认值);
appendfsync everysec
复制代码
- 设置后台启动
由于 Redis
默认是 前台启动,不建议使用。修改 Redis
配置文件,把 daemonize no
改为 daemonize yes
。
daemonize yes
复制代码
- 设置远程访问
Redis
默认只允许 本机访问,把 bind
修改为 bind 0.0.0.0
此设置会变成 允许所有远程访问。如果想指定限制访问,可设置对应的 IP
。
bind 0.0.0.0
复制代码
- 配置
Redis
日志记录
找到 logfile
配置,默认是:logfile ""
,改为自定义日志文件路径。
logfile /var/log/redis_6379.log
复制代码
- 设置
Redis
请求密码
把 requirepass
修改为 123456
,修改之后重启下服务
requirepass "123456"
复制代码
有了密码之后,进入客户端,就得这样访问:
$ redis-cli -h 127.0.0.1 -p 6379 -a 123456
复制代码
3.1.4. Redis的常用命令
- 启动命令
$ redis-server /usr/local/redis.conf
复制代码
- 关闭命令
$ redis-cli -h 127.0.0.1 -p 6379 shutdown
复制代码
- 查看是否启动
$ ps -ef | grep redis
复制代码
- 进入客户端
$ redis-cli
复制代码
- 关闭客户端
$ redis-cli shutdown
复制代码
注意:不建议使用
kill -9
,这种方式不但不会做持久化操作,还会造成缓冲区等资源不能优雅关闭。极端情况下造成AOF
和 复制丢失数据 的情况。shutdown
还有一个参数,代表是否在关闭redis
前,生成 持久化文件,命令为redis-cli shutdown nosave|save
。
- 设置为开机自动启动
$ echo "redis-server /usr/local/redis.conf" >> /etc/rc.local
复制代码
- 开放防火墙端口
# 添加规则
iptables -I INPUT -p tcp -m tcp --dport 6379 -j ACCEPT
# 保存规则
service iptables save
# 重启iptables
service iptables restart
复制代码
3.1.5. 注册Redis为系统服务
在 /etc/init.d
目录下添加 Redis
服务的 启动,暂停 和 重启 脚本:
$ sudo /etc/init.d/redis
复制代码
脚本的内容如下:
#!/bin/sh
#
# redis - this script starts and stops the redis-server daemon
#
# chkconfig: - 85 15
# description: Redis is a persistent key-value database
# processname: redis-server
# config: /usr/local/redis/bin/redis-server
# config: /etc/redis.conf
# Source function library.
. /etc/rc.d/init.d/functions
# Source networking configuration.
. /etc/sysconfig/network
# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0
redis="/usr/local/redis/bin/redis-server"
prog=$(basename $redis)
REDIS_CONF_FILE="/etc/redis.conf"
[ -f /etc/sysconfig/redis ] && . /etc/sysconfig/redis
lockfile=/var/lock/subsys/redis
start() {
[ -x $redis ] || exit 5
[ -f $REDIS_CONF_FILE ] || exit 6
echo -n $"Starting $prog: "
daemon $redis $REDIS_CONF_FILE
retval=$?
echo
[ $retval -eq 0 ] && touch $lockfile
return $retval
}
stop() {
echo -n $"Stopping $prog: "
killproc $prog -QUIT
retval=$?
echo
[ $retval -eq 0 ] && rm -f $lockfile
return $retval
}
restart() {
stop
start
}
reload() {
echo -n $"Reloading $prog: "
killproc $redis -HUP
RETVAL=$?
echo
}
force_reload() {
restart
}
rh_status() {
status $prog
}
rh_status_q() {
rh_status >/dev/null 2>&1
}
case "$1" in
start)
rh_status_q && exit 0
$1
;;
stop)
rh_status_q || exit 0
$1
;;
restart|configtest)
$1
;;
reload)
rh_status_q || exit 7
$1
;;
force-reload)
force_reload
;;
status)
rh_status
;;
condrestart|try-restart)
rh_status_q || exit 0
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|orce-reload}"
exit 2
esac
复制代码
赋予脚本文件可执行权限:
$ chmod 755 /etc/init.d/redis
复制代码
启动、停止和重启 redis
服务:
service redis start
service redis stop
service redis restart
复制代码
3.2. Redis主从复制集群安装
3.2.1. Redis-Server配置说明
Redis Master | 10.206.20.231 | 16379 |
Redis Slave | 10.206.20.231 | 26379 |
3.2.2. Redis主从架构配置
- 编辑 从机 的
Redis
配置文件,找到210
行(大概)-#slaveof <masterip> <masterport>
。去掉该注释,填写 主服务器 的IP
和 端口。
slaveof 10.206.20.231 16379
复制代码
- 如果 主服务器 设置了密码,还需要找到
masterauth <master-password>
这一行,去掉注释,改为masterauth
的主机密码。
masterauth 123456
复制代码
- 配置完成后重启 从服务器 的
Redis
服务。
$ service redis restart
复制代码
- 重启完成之后,进入 主服务器 的
redis-cli
模式下,命令为redis-cli -h 127.0.0.1 -p 16379 -a 123456
。输入INFO replication
查询到 当前主机 的Redis
的状态,连接上 主服务器 的 从服务器。
Redis
主服务器 的配置文件:
- redis.conf
daemonize yes
pidfile /var/run/redis-16379.pid
logfile /var/log/redis/redis-16379.log
port 16379
bind 0.0.0.0
timeout 300
databases 16
dbfilename dump-16379.db
dir ./redis-workdir
masterauth 123456
requirepass 123456
复制代码
Redis
从服务器 的配置文件:
- redis.conf
daemonize yes
pidfile /var/run/redis-26379.pid
logfile /var/log/redis/redis-26379.log
port 26379
bind 0.0.0.0
timeout 300
databases 16
dbfilename dump-26379.db
dir ./redis-workdir
masterauth 123456
requirepass 123456
slaveof 127.0.0.1 16379
复制代码
Redis
主服务器 的状态如下:
# Replication
role:master
connected_slaves:1
slave0:ip=10.206.20.231,port=16379,state=online,offset=28,lag=1
master_replid:625ae9f362643da5337835beaeabfdca426198c7
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:28
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:28
复制代码
Redis
从服务器 的状态如下:
# Replication
role:slave
master_host:10.206.20.231
master_port:26379
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_repl_offset:210
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:625ae9f362643da5337835beaeabfdca426198c7
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:210
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:210
复制代码
3.2.3. Redis主从配置验证
上面完成了基本的 主从配置,可以简单的测试一下效果:
- 进入 主服务器 的
redis-cli
模式,然后set
一个值,比如:
> set master_port "16379"
OK
复制代码
- 切换进入 从服务器 的
redis-cli
的模式,查询刚刚设置的值看是否存在:
> get master_port
"16379"
复制代码
此时,我们可以发现是可以获取到值的,Redis
的 主从模式 正常工作。
小结
本文简单的说明了 Redis
的相关 特性 和 应用场景,详细地给出 Redis
单服务器的 编译,安装,配置 和 启动,进一步引入了 Redis
主从复制 的相关原理和详细配置。关于 Redis
的 高可用机制 和 集群搭建,下文将给出详细的说明。
Redis哨兵模式与高可用集群
前言
Redis
的 主从复制 模式下,一旦 主节点 由于故障不能提供服务,需要手动将 从节点 晋升为 主节点,同时还要通知 客户端 更新 主节点地址,这种故障处理方式从一定程度上是无法接受的。Redis 2.8
以后提供了 Redis Sentinel
哨兵机制 来解决这个问题。
正文
1. Redis高可用概述
在 Web
服务器中,高可用 是指服务器可以 正常访问 的时间,衡量的标准是在 多长时间 内可以提供正常服务(99.9%
、99.99%
、99.999%
等等)。在 Redis
层面,高可用 的含义要宽泛一些,除了保证提供 正常服务(如 主从分离、快速容灾技术 等),还需要考虑 数据容量扩展、数据安全 等等。
在 Redis
中,实现 高可用 的技术主要包括 持久化、复制、哨兵 和 集群,下面简单说明它们的作用,以及解决了什么样的问题:
-
持久化:持久化是 最简单的 高可用方法。它的主要作用是 数据备份,即将数据存储在 硬盘,保证数据不会因进程退出而丢失。
-
复制:复制是高可用
Redis
的基础,哨兵 和 集群 都是在 复制基础 上实现高可用的。复制主要实现了数据的多机备份以及对于读操作的负载均衡和简单的故障恢复。缺陷是故障恢复无法自动化、写操作无法负载均衡、存储能力受到单机的限制。 -
哨兵:在复制的基础上,哨兵实现了 自动化 的 故障恢复。缺陷是 写操作 无法 负载均衡,存储能力 受到 单机 的限制。
-
集群:通过集群,
Redis
解决了 写操作 无法 负载均衡 以及 存储能力 受到 单机限制 的问题,实现了较为 完善 的 高可用方案。
2. Redis Sentinel的基本概念
Redis Sentinel
是 Redis
高可用 的实现方案。Sentinel
是一个管理多个 Redis
实例的工具,它可以实现对 Redis
的 监控、通知、自动故障转移。下面先对 Redis Sentinel
的 基本概念 进行简单的介绍。
基本名词说明:
Redis数据节点 | 主节点和从节点 | 主节点和从节点的进程 |
主节点(master) | Redis主数据库 | 一个独立的Redis进程 |
从节点(slave) | Redis从数据库 | 一个独立的Redis进程 |
Sentinel节点 | 监控Redis数据节点 | 一个独立的Sentinel进程 |
Sentinel节点集合 | 若干Sentinel节点的抽象组合 | 若干Sentinel节点进程 |
Redis Sentinel | Redis高可用实现方案 | Sentinel节点集合和Redis数据节点进程 |
应用客户端 | 泛指一个或多个客户端 | 一个或者多个客户端进程或者线程 |
如图所示,Redis
的 主从复制模式 和 Sentinel
高可用架构 的示意图:
3. Redis主从复制的问题
Redis
主从复制 可将 主节点 数据同步给 从节点,从节点此时有两个作用:
- 一旦 主节点宕机,从节点 作为 主节点 的 备份 可以随时顶上来。
- 扩展 主节点 的 读能力,分担主节点读压力。
主从复制 同时存在以下几个问题:
-
一旦 主节点宕机,从节点 晋升成 主节点,同时需要修改 应用方 的 主节点地址,还需要命令所有 从节点 去 复制 新的主节点,整个过程需要 人工干预。
-
主节点 的 写能力 受到 单机的限制。
-
主节点 的 存储能力 受到 单机的限制。
-
原生复制 的弊端在早期的版本中也会比较突出,比如:
Redis
复制中断 后,从节点 会发起psync
。此时如果 同步不成功,则会进行 全量同步,主库 执行 全量备份 的同时,可能会造成毫秒或秒级的 卡顿。
4. Redis Sentinel深入探究
4.1. Redis Sentinel的架构
4.2. Redis Sentinel的主要功能
Sentinel
的主要功能包括 主节点存活检测、主从运行情况检测、自动故障转移 (failover
)、主从切换。Redis
的 Sentinel
最小配置是 一主一从。
Redis
的 Sentinel
系统可以用来管理多个 Redis
服务器,该系统可以执行以下四个任务:
- 监控
Sentinel
会不断的检查 主服务器 和 从服务器 是否正常运行。
- 通知
当被监控的某个 Redis
服务器出现问题,Sentinel
通过 API
脚本 向 管理员 或者其他的 应用程序 发送通知。
- 自动故障转移
当 主节点 不能正常工作时,Sentinel
会开始一次 自动的 故障转移操作,它会将与 失效主节点 是 主从关系 的其中一个 从节点 升级为新的 主节点,并且将其他的 从节点 指向 新的主节点。
- 配置提供者
在 Redis Sentinel
模式下,客户端应用 在初始化时连接的是 Sentinel
节点集合,从中获取 主节点 的信息。
4.3. 主观下线和客观下线
默认情况下,每个 Sentinel
节点会以 每秒一次 的频率对 Redis
节点和 其它 的 Sentinel
节点发送 PING
命令,并通过节点的 回复 来判断节点是否在线。
- 主观下线
主观下线 适用于所有 主节点 和 从节点。如果在 down-after-milliseconds
毫秒内,Sentinel
没有收到 目标节点 的有效回复,则会判定 该节点 为 主观下线。
- 客观下线
客观下线 只适用于 主节点。如果 主节点 出现故障,Sentinel
节点会通过 sentinel is-master-down-by-addr
命令,向其它 Sentinel
节点询问对该节点的 状态判断。如果超过 <quorum>
个数的节点判定 主节点 不可达,则该 Sentinel
节点会判断 主节点 为 客观下线。
4.4. Sentinel的通信命令
Sentinel
节点连接一个 Redis
实例的时候,会创建 cmd
和 pub/sub
两个 连接。Sentinel
通过 cmd
连接给 Redis
发送命令,通过 pub/sub
连接到 Redis
实例上的其他 Sentinel
实例。
Sentinel
与 Redis
主节点 和 从节点 交互的命令,主要包括:
PING |
Sentinel 向 Redis 节点发送 PING 命令,检查节点的状态 |
INFO |
Sentinel 向 Redis 节点发送 INFO 命令,获取它的 从节点信息
|
PUBLISH |
Sentinel 向其监控的 Redis 节点 __sentinel__:hello 这个 channel 发布 自己的信息 及 主节点 相关的配置 |
SUBSCRIBE |
Sentinel 通过订阅 Redis 主节点 和 从节点 的 __sentinel__:hello 这个 channnel ,获取正在监控相同服务的其他 Sentinel 节点 |
Sentinel
与 Sentinel
交互的命令,主要包括:
PING |
Sentinel 向其他 Sentinel 节点发送 PING 命令,检查节点的状态 |
SENTINEL:is-master-down-by-addr | 和其他 Sentinel 协商 主节点 的状态,如果 主节点 处于 SDOWN 状态,则投票自动选出新的 主节点
|
4.5. Redis Sentinel的工作原理
每个 Sentinel
节点都需要 定期执行 以下任务:
- 每个
Sentinel
以 每秒钟 一次的频率,向它所知的 主服务器、从服务器 以及其他Sentinel
实例 发送一个PING
命令。
- 如果一个 实例(
instance
)距离 最后一次 有效回复PING
命令的时间超过down-after-milliseconds
所指定的值,那么这个实例会被Sentinel
标记为 主观下线。
- 如果一个 主服务器 被标记为 主观下线,那么正在 监视 这个 主服务器 的所有
Sentinel
节点,要以 每秒一次 的频率确认 主服务器 的确进入了 主观下线 状态。
- 如果一个 主服务器 被标记为 主观下线,并且有 足够数量 的
Sentinel
(至少要达到 配置文件 指定的数量)在指定的 时间范围 内同意这一判断,那么这个 主服务器 被标记为 客观下线。
- 在一般情况下, 每个
Sentinel
会以每10
秒一次的频率,向它已知的所有 主服务器 和 从服务器 发送INFO
命令。当一个 主服务器 被Sentinel
标记为 客观下线 时,Sentinel
向 下线主服务器 的所有 从服务器 发送INFO
命令的频率,会从10
秒一次改为 每秒一次。
-
Sentinel
和其他Sentinel
协商 主节点 的状态,如果 主节点 处于SDOWN
状态,则投票自动选出新的 主节点。将剩余的 从节点 指向 新的主节点 进行 数据复制。
- 当没有足够数量的
Sentinel
同意 主服务器 下线时, 主服务器 的 客观下线状态 就会被移除。当 主服务器 重新向Sentinel
的PING
命令返回 有效回复 时,主服务器 的 主观下线状态 就会被移除。
注意:一个有效的
PING
回复可以是:+PONG
、-LOADING
或者-MASTERDOWN
。如果 服务器 返回除以上三种回复之外的其他回复,又或者在 指定时间 内没有回复PING
命令, 那么Sentinel
认为服务器返回的回复 无效(non-valid
)。
5. Redis Sentinel搭建
5.1. Redis Sentinel的部署须知
-
一个稳健的
Redis Sentinel
集群,应该使用至少 三个Sentinel
实例,并且保证讲这些实例放到 不同的机器 上,甚至不同的 物理区域。 -
Sentinel
无法保证 强一致性。 -
常见的 客户端应用库 都支持
Sentinel
。 -
Sentinel
需要通过不断的 测试 和 观察,才能保证高可用。
5.2. Redis Sentinel的配置文件
# 哨兵sentinel实例运行的端口,默认26379
port 26379
# 哨兵sentinel的工作目录
dir ./
# 哨兵sentinel监控的redis主节点的
## ip:主机ip地址
## port:哨兵端口号
## master-name:可以自己命名的主节点名字(只能由字母A-z、数字0-9 、这三个字符".-_"组成。)
## quorum:当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 2
# 当在Redis实例中开启了requirepass <foobared>,所有连接Redis实例的客户端都要提供密码。
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster 123456
# 指定主节点应答哨兵sentinel的最大时间间隔,超过这个时间,哨兵主观上认为主节点下线,默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000
# 指定了在发生failover主备切换时,最多可以有多少个slave同时对新的master进行同步。这个数字越小,完成failover所需的时间就越长;反之,但是如果这个数字越大,就意味着越多的slave因为replication而不可用。可以通过将这个值设为1,来保证每次只有一个slave,处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1
# 故障转移的超时时间failover-timeout,默认三分钟,可以用在以下这些方面:
## 1. 同一个sentinel对同一个master两次failover之间的间隔时间。
## 2. 当一个slave从一个错误的master那里同步数据时开始,直到slave被纠正为从正确的master那里同步数据时结束。
## 3. 当想要取消一个正在进行的failover时所需要的时间。
## 4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来同步数据了
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 180000
# 当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本。一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
# 对于脚本的运行结果有以下规则:
## 1. 若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10。
## 2. 若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
## 3. 如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
# sentinel notification-script <master-name> <script-path>
sentinel notification-script mymaster /var/redis/notify.sh
# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
复制代码
5.3. Redis Sentinel的节点规划
Redis Master | 10.206.20.231 | 16379 |
Redis Slave1 | 10.206.20.231 | 26379 |
Redis Slave2 | 10.206.20.231 | 36379 |
Redis Sentinel1 | 10.206.20.231 | 16380 |
Redis Sentinel2 | 10.206.20.231 | 26380 |
Redis Sentinel3 | 10.206.20.231 | 36380 |
5.4. Redis Sentinel的配置搭建
5.4.1. Redis-Server的配置管理
分别拷贝三份 redis.conf
文件到 /usr/local/redis-sentinel
目录下面。三个配置文件分别对应 master
、slave1
和 slave2
三个 Redis
节点的 启动配置。
$ sudo cp /usr/local/redis-4.0.11/redis.conf /usr/local/redis-sentinel/redis-16379.conf
$ sudo cp /usr/local/redis-4.0.11/redis.conf /usr/local/redis-sentinel/redis-26379.conf
$ sudo cp /usr/local/redis-4.0.11/redis.conf /usr/local/redis-sentinel/redis-36379.conf
复制代码
分别修改三份配置文件如下:
- 主节点:redis-16379.conf
daemonize yes
pidfile /var/run/redis-16379.pid
logfile /var/log/redis/redis-16379.log
port 16379
bind 0.0.0.0
timeout 300
databases 16
dbfilename dump-16379.db
dir ./redis-workdir
masterauth 123456
requirepass 123456
复制代码
- 从节点1:redis-26379.conf
daemonize yes
pidfile /var/run/redis-26379.pid
logfile /var/log/redis/redis-26379.log
port 26379
bind 0.0.0.0
timeout 300
databases 16
dbfilename dump-26379.db
dir ./redis-workdir
masterauth 123456
requirepass 123456
slaveof 127.0.0.1 16379
复制代码
- 从节点2:redis-36379.conf
daemonize yes
pidfile /var/run/redis-36379.pid
logfile /var/log/redis/redis-36379.log
port 36379
bind 0.0.0.0
timeout 300
databases 16
dbfilename dump-36379.db
dir ./redis-workdir
masterauth 123456
requirepass 123456
slaveof 127.0.0.1 16379
复制代码
如果要做 自动故障转移,建议所有的
redis.conf
都设置masterauth
。因为 自动故障 只会重写 主从关系,即slaveof
,不会自动写入masterauth
。如果Redis
原本没有设置密码,则可以忽略。
5.4.2. Redis-Server启动验证
按顺序分别启动 16379
,26379
和 36379
三个 Redis
节点,启动命令和启动日志如下:
Redis
的启动命令:
$ sudo redis-server /usr/local/redis-sentinel/redis-16379.conf
$ sudo redis-server /usr/local/redis-sentinel/redis-26379.conf
$ sudo redis-server /usr/local/redis-sentinel/redis-36379.conf
复制代码
查看 Redis
的启动进程:
$ ps -ef | grep redis-server
0 7127 1 0 2:16下午 ?? 0:01.84 redis-server 0.0.0.0:16379
0 7133 1 0 2:16下午 ?? 0:01.73 redis-server 0.0.0.0:26379
0 7137 1 0 2:16下午 ?? 0:01.70 redis-server 0.0.0.0:36379
复制代码
查看 Redis
的启动日志:
- 节点
redis-16379
$ cat /var/log/redis/redis-16379.log
7126:C 22 Aug 14:16:38.907 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7126:C 22 Aug 14:16:38.908 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7126, just started
7126:C 22 Aug 14:16:38.908 # Configuration loaded
7127:M 22 Aug 14:16:38.910 * Increased maximum number of open files to 10032 (it was originally set to 256).
7127:M 22 Aug 14:16:38.912 * Running mode=standalone, port=16379.
7127:M 22 Aug 14:16:38.913 # Server initialized
7127:M 22 Aug 14:16:38.913 * Ready to accept connections
7127:M 22 Aug 14:16:48.416 * Slave 127.0.0.1:26379 asks for synchronization
7127:M 22 Aug 14:16:48.416 * Full resync requested by slave 127.0.0.1:26379
7127:M 22 Aug 14:16:48.416 * Starting BGSAVE for SYNC with target: disk
7127:M 22 Aug 14:16:48.416 * Background saving started by pid 7134
7134:C 22 Aug 14:16:48.433 * DB saved on disk
7127:M 22 Aug 14:16:48.487 * Background saving terminated with success
7127:M 22 Aug 14:16:48.494 * Synchronization with slave 127.0.0.1:26379 succeeded
7127:M 22 Aug 14:16:51.848 * Slave 127.0.0.1:36379 asks for synchronization
7127:M 22 Aug 14:16:51.849 * Full resync requested by slave 127.0.0.1:36379
7127:M 22 Aug 14:16:51.849 * Starting BGSAVE for SYNC with target: disk
7127:M 22 Aug 14:16:51.850 * Background saving started by pid 7138
7138:C 22 Aug 14:16:51.862 * DB saved on disk
7127:M 22 Aug 14:16:51.919 * Background saving terminated with success
7127:M 22 Aug 14:16:51.923 * Synchronization with slave 127.0.0.1:36379 succeeded
复制代码
以下两行日志日志表明,redis-16379
作为 Redis
的 主节点,redis-26379
和 redis-36379
作为 从节点,从 主节点 同步数据。
7127:M 22 Aug 14:16:48.416 * Slave 127.0.0.1:26379 asks for synchronization
7127:M 22 Aug 14:16:51.848 * Slave 127.0.0.1:36379 asks for synchronization
复制代码
- 节点
redis-26379
$ cat /var/log/redis/redis-26379.log
7132:C 22 Aug 14:16:48.407 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7132:C 22 Aug 14:16:48.408 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7132, just started
7132:C 22 Aug 14:16:48.408 # Configuration loaded
7133:S 22 Aug 14:16:48.410 * Increased maximum number of open files to 10032 (it was originally set to 256).
7133:S 22 Aug 14:16:48.412 * Running mode=standalone, port=26379.
7133:S 22 Aug 14:16:48.413 # Server initialized
7133:S 22 Aug 14:16:48.413 * Ready to accept connections
7133:S 22 Aug 14:16:48.413 * Connecting to MASTER 127.0.0.1:16379
7133:S 22 Aug 14:16:48.413 * MASTER <-> SLAVE sync started
7133:S 22 Aug 14:16:48.414 * Non blocking connect for SYNC fired the event.
7133:S 22 Aug 14:16:48.414 * Master replied to PING, replication can continue...
7133:S 22 Aug 14:16:48.415 * Partial resynchronization not possible (no cached master)
7133:S 22 Aug 14:16:48.417 * Full resync from master: 211d3b4eceaa3af4fe5c77d22adf06e1218e0e7b:0
7133:S 22 Aug 14:16:48.494 * MASTER <-> SLAVE sync: receiving 176 bytes from master
7133:S 22 Aug 14:16:48.495 * MASTER <-> SLAVE sync: Flushing old data
7133:S 22 Aug 14:16:48.496 * MASTER <-> SLAVE sync: Loading DB in memory
7133:S 22 Aug 14:16:48.498 * MASTER <-> SLAVE sync: Finished with success
复制代码
- 节点
redis-36379
$ cat /var/log/redis/redis-36379.log
7136:C 22 Aug 14:16:51.839 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7136:C 22 Aug 14:16:51.840 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7136, just started
7136:C 22 Aug 14:16:51.841 # Configuration loaded
7137:S 22 Aug 14:16:51.843 * Increased maximum number of open files to 10032 (it was originally set to 256).
7137:S 22 Aug 14:16:51.845 * Running mode=standalone, port=36379.
7137:S 22 Aug 14:16:51.845 # Server initialized
7137:S 22 Aug 14:16:51.846 * Ready to accept connections
7137:S 22 Aug 14:16:51.846 * Connecting to MASTER 127.0.0.1:16379
7137:S 22 Aug 14:16:51.847 * MASTER <-> SLAVE sync started
7137:S 22 Aug 14:16:51.847 * Non blocking connect for SYNC fired the event.
7137:S 22 Aug 14:16:51.847 * Master replied to PING, replication can continue...
7137:S 22 Aug 14:16:51.848 * Partial resynchronization not possible (no cached master)
7137:S 22 Aug 14:16:51.850 * Full resync from master: 211d3b4eceaa3af4fe5c77d22adf06e1218e0e7b:14
7137:S 22 Aug 14:16:51.923 * MASTER <-> SLAVE sync: receiving 176 bytes from master
7137:S 22 Aug 14:16:51.923 * MASTER <-> SLAVE sync: Flushing old data
7137:S 22 Aug 14:16:51.924 * MASTER <-> SLAVE sync: Loading DB in memory
7137:S 22 Aug 14:16:51.927 * MASTER <-> SLAVE sync: Finished with success
复制代码
5.4.3. Sentinel的配置管理
分别拷贝三份 redis-sentinel.conf
文件到 /usr/local/redis-sentinel
目录下面。三个配置文件分别对应 master
、slave1
和 slave2
三个 Redis
节点的 哨兵配置。
$ sudo cp /usr/local/redis-4.0.11/sentinel.conf /usr/local/redis-sentinel/sentinel-16380.conf
$ sudo cp /usr/local/redis-4.0.11/sentinel.conf /usr/local/redis-sentinel/sentinel-26380.conf
$ sudo cp /usr/local/redis-4.0.11/sentinel.conf /usr/local/redis-sentinel/sentinel-36380.conf
复制代码
- 节点1:sentinel-16380.conf
protected-mode no
bind 0.0.0.0
port 16380
daemonize yes
sentinel monitor master 127.0.0.1 16379 2
sentinel down-after-milliseconds master 5000
sentinel failover-timeout master 180000
sentinel parallel-syncs master 1
sentinel auth-pass master 123456
logfile /var/log/redis/sentinel-16380.log
复制代码
- 节点2:sentinel-26380.conf
protected-mode no
bind 0.0.0.0
port 26380
daemonize yes
sentinel monitor master 127.0.0.1 16379 2
sentinel down-after-milliseconds master 5000
sentinel failover-timeout master 180000
sentinel parallel-syncs master 1
sentinel auth-pass master 123456
logfile /var/log/redis/sentinel-26380.log
复制代码
- 节点3:sentinel-36380.conf
protected-mode no
bind 0.0.0.0
port 36380
daemonize yes
sentinel monitor master 127.0.0.1 16379 2
sentinel down-after-milliseconds master 5000
sentinel failover-timeout master 180000
sentinel parallel-syncs master 1
sentinel auth-pass master 123456
logfile /var/log/redis/sentinel-36380.log
复制代码
5.4.4. Sentinel启动验证
按顺序分别启动 16380
,26380
和 36380
三个 Sentinel
节点,启动命令和启动日志如下:
$ sudo redis-sentinel /usr/local/redis-sentinel/sentinel-16380.conf
$ sudo redis-sentinel /usr/local/redis-sentinel/sentinel-26380.conf
$ sudo redis-sentinel /usr/local/redis-sentinel/sentinel-36380.conf
复制代码
查看 Sentinel
的启动进程:
$ ps -ef | grep redis-sentinel
0 7954 1 0 3:30下午 ?? 0:00.05 redis-sentinel 0.0.0.0:16380 [sentinel]
0 7957 1 0 3:30下午 ?? 0:00.05 redis-sentinel 0.0.0.0:26380 [sentinel]
0 7960 1 0 3:30下午 ?? 0:00.04 redis-sentinel 0.0.0.0:36380 [sentinel]
复制代码
查看 Sentinel
的启动日志:
- 节点
sentinel-16380
$ cat /var/log/redis/sentinel-16380.log
7953:X 22 Aug 15:30:27.245 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7953:X 22 Aug 15:30:27.245 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7953, just started
7953:X 22 Aug 15:30:27.245 # Configuration loaded
7954:X 22 Aug 15:30:27.247 * Increased maximum number of open files to 10032 (it was originally set to 256).
7954:X 22 Aug 15:30:27.249 * Running mode=sentinel, port=16380.
7954:X 22 Aug 15:30:27.250 # Sentinel ID is 69d05b86a82102a8919231fd3c2d1f21ce86e000
7954:X 22 Aug 15:30:27.250 # +monitor master master 127.0.0.1 16379 quorum 2
7954:X 22 Aug 15:30:32.286 # +sdown sentinel fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 127.0.0.1 36380 @ master 127.0.0.1 16379
7954:X 22 Aug 15:30:34.588 # -sdown sentinel fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 127.0.0.1 36380 @ master 127.0.0.1 16379
复制代码
sentinel-16380
节点的 Sentinel ID
为 69d05b86a82102a8919231fd3c2d1f21ce86e000
,并通过 Sentinel ID
把自身加入 sentinel
集群中。
- 节点
sentinel-26380
$ cat /var/log/redis/sentinel-26380.log
7956:X 22 Aug 15:30:30.900 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7956:X 22 Aug 15:30:30.901 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7956, just started
7956:X 22 Aug 15:30:30.901 # Configuration loaded
7957:X 22 Aug 15:30:30.904 * Increased maximum number of open files to 10032 (it was originally set to 256).
7957:X 22 Aug 15:30:30.905 * Running mode=sentinel, port=26380.
7957:X 22 Aug 15:30:30.906 # Sentinel ID is 21e30244cda6a3d3f55200bcd904d0877574e506
7957:X 22 Aug 15:30:30.906 # +monitor master master 127.0.0.1 16379 quorum 2
7957:X 22 Aug 15:30:30.907 * +slave slave 127.0.0.1:26379 127.0.0.1 26379 @ master 127.0.0.1 16379
7957:X 22 Aug 15:30:30.911 * +slave slave 127.0.0.1:36379 127.0.0.1 36379 @ master 127.0.0.1 16379
7957:X 22 Aug 15:30:36.311 * +sentinel sentinel fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 127.0.0.1 36380 @ master 127.0.0.1 16379
复制代码
sentinel-26380
节点的 Sentinel ID
为 21e30244cda6a3d3f55200bcd904d0877574e506
,并通过 Sentinel ID
把自身加入 sentinel
集群中。此时 sentinel
集群中已有 sentinel-16380
和 sentinel-26380
两个节点。
- 节点
sentinel-36380
$ cat /var/log/redis/sentinel-36380.log
7959:X 22 Aug 15:30:34.273 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7959:X 22 Aug 15:30:34.274 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7959, just started
7959:X 22 Aug 15:30:34.274 # Configuration loaded
7960:X 22 Aug 15:30:34.276 * Increased maximum number of open files to 10032 (it was originally set to 256).
7960:X 22 Aug 15:30:34.277 * Running mode=sentinel, port=36380.
7960:X 22 Aug 15:30:34.278 # Sentinel ID is fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7
7960:X 22 Aug 15:30:34.278 # +monitor master master 127.0.0.1 16379 quorum 2
7960:X 22 Aug 15:30:34.279 * +slave slave 127.0.0.1:26379 127.0.0.1 26379 @ master 127.0.0.1 16379
7960:X 22 Aug 15:30:34.283 * +slave slave 127.0.0.1:36379 127.0.0.1 36379 @ master 127.0.0.1 16379
7960:X 22 Aug 15:30:34.993 * +sentinel sentinel 21e30244cda6a3d3f55200bcd904d0877574e506 127.0.0.1 26380 @ master 127.0.0.1 16379
复制代码
sentinel-36380
节点的 Sentinel ID
为 fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7
,并通过 Sentinel ID
把自身加入 sentinel
集群中。此时 sentinel
集群中已有 sentinel-16380
,sentinel-26380
和 sentinel-36380
三个节点。
5.4.5. Sentinel配置刷新
- 节点1:sentinel-16380.conf
sentinel-16380.conf
文件新生成如下的配置项:
# Generated by CONFIG REWRITE
dir "/usr/local/redis-sentinel"
sentinel config-epoch master 0
sentinel leader-epoch master 0
sentinel known-slave master 127.0.0.1 36379
sentinel known-slave master 127.0.0.1 26379
sentinel known-sentinel master 127.0.0.1 26380 21e30244cda6a3d3f55200bcd904d0877574e506
sentinel known-sentinel master 127.0.0.1 36380 fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7
sentinel current-epoch 0
复制代码
可以注意到,sentinel-16380.conf
刷新写入了 Redis
主节点关联的所有 从节点 redis-26379
和 redis-36379
,同时写入了其余两个 Sentinel
节点 sentinel-26380
和 sentinel-36380
的 IP
地址,端口号 和 Sentinel ID
。
# Generated by CONFIG REWRITE
dir "/usr/local/redis-sentinel"
sentinel config-epoch master 0
sentinel leader-epoch master 0
sentinel known-slave master 127.0.0.1 26379
sentinel known-slave master 127.0.0.1 36379
sentinel known-sentinel master 127.0.0.1 36380 fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7
sentinel known-sentinel master 127.0.0.1 16380 69d05b86a82102a8919231fd3c2d1f21ce86e000
sentinel current-epoch 0
复制代码
可以注意到,sentinel-26380.conf
刷新写入了 Redis
主节点关联的所有 从节点 redis-26379
和 redis-36379
,同时写入了其余两个 Sentinel
节点 sentinel-36380
和 sentinel-16380
的 IP
地址,端口号 和 Sentinel ID
。
# Generated by CONFIG REWRITE
dir "/usr/local/redis-sentinel"
sentinel config-epoch master 0
sentinel leader-epoch master 0
sentinel known-slave master 127.0.0.1 36379
sentinel known-slave master 127.0.0.1 26379
sentinel known-sentinel master 127.0.0.1 16380 69d05b86a82102a8919231fd3c2d1f21ce86e000
sentinel known-sentinel master 127.0.0.1 26380 21e30244cda6a3d3f55200bcd904d0877574e506
sentinel current-epoch 0
复制代码
可以注意到,sentinel-36380.conf
刷新写入了 Redis
主节点关联的所有 从节点 redis-26379
和 redis-36379
,同时写入了其余两个 Sentinel
节点 sentinel-16380
和 sentinel-26380
的 IP
地址,端口号 和 Sentinel ID
。
5.5. Sentinel时客户端命令
- 检查其他
Sentinel
节点的状态,返回PONG
为正常。
> PING sentinel
复制代码
- 显示被监控的所有 主节点 以及它们的状态。
> SENTINEL masters
复制代码
- 显示指定 主节点 的信息和状态。
> SENTINEL master <master_name>
复制代码
- 显示指定 主节点 的所有 从节点 以及它们的状态。
> SENTINEL slaves <master_name>
复制代码
返回指定 主节点 的 IP
地址和 端口。如果正在进行 failover
或者 failover
已经完成,将会显示被提升为 主节点 的 从节点 的 IP
地址和 端口。
> SENTINEL get-master-addr-by-name <master_name>
复制代码
- 重置名字匹配该 正则表达式 的所有的 主节点 的状态信息,清除它之前的 状态信息,以及 从节点 的信息。
> SENTINEL reset <pattern>
复制代码
- 强制当前
Sentinel
节点执行failover
,并且不需要得到其他Sentinel
节点的同意。但是failover
后会将 最新的配置 发送给其他Sentinel
节点。
SENTINEL failover <master_name>
复制代码
6. Redis Sentinel故障切换与恢复
6.1. Redis CLI客户端跟踪
上面的日志显示,redis-16379
节点为 主节点,它的进程 ID
为 7127
。为了模拟 Redis
主节点故障,强制杀掉这个进程。
$ kill -9 7127
复制代码
使用 redis-cli
客户端命令进入 sentinel-16380
节点,查看 Redis
节点 的状态信息。
$ redis-cli -p 16380
复制代码
- 查看
Redis
主从集群的 主节点 信息。可以发现redis-26379
晋升为 新的主节点。
127.0.0.1:16380> SENTINEL master master
1) "name"
2) "master"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "26379"
7) "runid"
8) "b8ca3b468a95d1be5efe1f50c50636cafe48c59f"
9) "flags"
10) "master"
11) "link-pending-commands"
12) "0"
13) "link-refcount"
14) "1"
15) "last-ping-sent"
16) "0"
17) "last-ok-ping-reply"
18) "588"
19) "last-ping-reply"
20) "588"
21) "down-after-milliseconds"
22) "5000"
23) "info-refresh"
24) "9913"
25) "role-reported"
26) "master"
27) "role-reported-time"
28) "663171"
29) "config-epoch"
30) "1"
31) "num-slaves"
32) "2"
33) "num-other-sentinels"
34) "2"
35) "quorum"
36) "2"
37) "failover-timeout"
38) "180000"
39) "parallel-syncs"
40) "1"
复制代码
6.2. Redis Sentinel日志跟踪
查看任意 Sentinel
节点的日志如下:
7954:X 22 Aug 18:40:22.504 # +tilt #tilt mode entered
7954:X 22 Aug 18:40:32.197 # +tilt #tilt mode entered
7954:X 22 Aug 18:41:02.241 # -tilt #tilt mode exited
7954:X 22 Aug 18:48:24.550 # +sdown master master 127.0.0.1 16379
7954:X 22 Aug 18:48:24.647 # +new-epoch 1
7954:X 22 Aug 18:48:24.651 # +vote-for-leader fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 1
7954:X 22 Aug 18:48:25.678 # +odown master master 127.0.0.1 16379 #quorum 3/2
7954:X 22 Aug 18:48:25.678 # Next failover delay: I will not start a failover before Wed Aug 22 18:54:24 2018
7954:X 22 Aug 18:48:25.709 # +config-update-from sentinel fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 127.0.0.1 36380 @ master 127.0.0.1 16379
7954:X 22 Aug 18:48:25.710 # +switch-master master 127.0.0.1 16379 127.0.0.1 26379
7954:X 22 Aug 18:48:25.710 * +slave slave 127.0.0.1:36379 127.0.0.1 36379 @ master 127.0.0.1 26379
7954:X 22 Aug 18:48:25.711 * +slave slave 127.0.0.1:16379 127.0.0.1 16379 @ master 127.0.0.1 26379
7954:X 22 Aug 18:48:30.738 # +sdown slave 127.0.0.1:16379 127.0.0.1 16379 @ master 127.0.0.1 26379
7954:X 22 Aug 19:38:23.479 # -sdown slave 127.0.0.1:16379 127.0.0.1 16379 @ master 127.0.0.1 26379
复制代码
- 分析日志,可以发现
redis-16329
节点先进入sdown
主观下线 状态。
+sdown master master 127.0.0.1 16379
复制代码
- 哨兵检测到
redis-16329
出现故障,Sentinel
进入一个 新纪元,从0
变为1
。
+new-epoch 1
复制代码
- 三个
Sentinel
节点开始协商 主节点 的状态,判断其是否需要 客观下线。
+vote-for-leader fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 1
复制代码
- 超过
quorum
个数的Sentinel
节点认为 主节点 出现故障,redis-16329
节点进入 客观下线 状态。
+odown master master 127.0.0.1 16379 #quorum 3/2
复制代码
-
Sentinal
进行 自动故障切换,协商选定redis-26329
节点作为新的 主节点。
+switch-master master 127.0.0.1 16379 127.0.0.1 26379
复制代码
-
redis-36329
节点和已经 客观下线 的redis-16329
节点成为redis-26479
的 从节点。
7954:X 22 Aug 18:48:25.710 * +slave slave 127.0.0.1:36379 127.0.0.1 36379 @ master 127.0.0.1 26379
7954:X 22 Aug 18:48:25.711 * +slave slave 127.0.0.1:16379 127.0.0.1 16379 @ master 127.0.0.1 26379
复制代码
6.3. Redis的配置文件
分别查看三个 redis
节点的配置文件,发生 主从切换 时 redis.conf
的配置会自动发生刷新。
- 节点 redis-16379
daemonize yes
pidfile "/var/run/redis-16379.pid"
logfile "/var/log/redis/redis-16379.log"
port 16379
bind 0.0.0.0
timeout 300
databases 16
dbfilename "dump-16379.db"
dir "/usr/local/redis-sentinel/redis-workdir"
masterauth "123456"
requirepass "123456"
复制代码
- 节点 redis-26379
daemonize yes
pidfile "/var/run/redis-26379.pid"
logfile "/var/log/redis/redis-26379.log"
port 26379
bind 0.0.0.0
timeout 300
databases 16
dbfilename "dump-26379.db"
dir "/usr/local/redis-sentinel/redis-workdir"
masterauth "123456"
requirepass "123456"
复制代码
- 节点 redis-36379
daemonize yes
pidfile "/var/run/redis-36379.pid"
logfile "/var/log/redis/redis-36379.log"
port 36379
bind 0.0.0.0
timeout 300
databases 16
dbfilename "dump-36379.db"
dir "/usr/local/redis-sentinel/redis-workdir"
masterauth "123456"
requirepass "123456"
slaveof 127.0.0.1 26379
复制代码
分析:
redis-26379
节点slaveof
配置被移除,晋升为 主节点。redis-16379
节点处于 宕机状态。redis-36379
的slaveof
配置更新为127.0.0.1 redis-26379
,成为redis-26379
的 从节点。
重启节点 redis-16379
。待正常启动后,再次查看它的 redis.conf
文件,配置如下:
daemonize yes
pidfile "/var/run/redis-16379.pid"
logfile "/var/log/redis/redis-16379.log"
port 16379
bind 0.0.0.0
timeout 300
databases 16
dbfilename "dump-16379.db"
dir "/usr/local/redis-sentinel/redis-workdir"
masterauth "123456"
requirepass "123456"
# Generated by CONFIG REWRITE
slaveof 127.0.0.1 26379
复制代码
节点 redis-16379
的配置文件新增一行 slaveof
配置属性,指向 redis-26379
,即成为 新的主节点 的 从节点。
小结
本文首先对 Redis
实现高可用的几种模式做出了阐述,指出了 Redis
主从复制 的不足之处,进一步引入了 Redis Sentinel
哨兵模式 的相关概念,深入说明了 Redis Sentinel
的 具体功能,基本原理,高可用搭建 和 自动故障切换 验证等。
当然,Redis Sentinel
仅仅解决了 高可用 的问题,对于 主节点 单点写入和单节点无法扩容等问题,还需要引入 Redis Cluster
集群模式 予以解决。
Redis集群模式搭建与原理详解
前言
在 Redis 3.0
之前,使用 哨兵(sentinel
)机制来监控各个节点之间的状态。Redis Cluster
是 Redis
的 分布式解决方案,在 3.0
版本正式推出,有效地解决了 Redis
在 分布式 方面的需求。当遇到 单机内存、并发、流量 等瓶颈时,可以采用 Cluster
架构方案达到 负载均衡 的目的。
Redis Cluster
。
正文
1. Redis集群方案
Redis Cluster
集群模式通常具有 高可用、可扩展性、分布式、容错 等特性。Redis
分布式方案一般有两种:
1.1 客户端分区方案
客户端 就已经决定数据会被 存储 到哪个 redis
节点或者从哪个 redis
节点 读取数据。其主要思想是采用 哈希算法 将 Redis
数据的 key
进行散列,通过 hash
函数,特定的 key
会 映射 到特定的 Redis
节点上。
客户端分区方案 的代表为 Redis Sharding
,Redis Sharding
是 Redis Cluster
出来之前,业界普遍使用的 Redis
多实例集群 方法。Java
的 Redis
客户端驱动库 Jedis
,支持 Redis Sharding
功能,即 ShardedJedis
以及 结合缓存池 的 ShardedJedisPool
。
- 优点
不使用 第三方中间件,分区逻辑 可控,配置 简单,节点之间无关联,容易 线性扩展,灵活性强。
- 缺点
客户端 无法 动态增删 服务节点,客户端需要自行维护 分发逻辑,客户端之间 无连接共享,会造成 连接浪费。
1.2. 代理分区方案
客户端 发送请求到一个 代理组件,代理 解析 客户端 的数据,并将请求转发至正确的节点,最后将结果回复给客户端。
-
优点:简化 客户端 的分布式逻辑,客户端 透明接入,切换成本低,代理的 转发 和 存储 分离。
-
缺点:多了一层 代理层,加重了 架构部署复杂度 和 性能损耗。
代理分区 主流实现的有方案有 Twemproxy
和 Codis
。
1.2.1. Twemproxy
Twemproxy
也叫 nutcraker
,是 twitter
开源的一个 redis
和 memcache
的 中间代理服务器 程序。Twemproxy
作为 代理,可接受来自多个程序的访问,按照 路由规则,转发给后台的各个 Redis
服务器,再原路返回。Twemproxy
存在 单点故障 问题,需要结合 Lvs
和 Keepalived
做 高可用方案。
-
优点:应用范围广,稳定性较高,中间代理层 高可用。
-
缺点:无法平滑地 水平扩容/缩容,无 可视化管理界面,运维不友好,出现故障,不能 自动转移。
1.2.2. Codis
Codis
是一个 分布式 Redis
解决方案,对于上层应用来说,连接 Codis-Proxy
和直接连接 原生的 Redis-Server
没有的区别。Codis
底层会 处理请求的转发,不停机的进行 数据迁移 等工作。Codis
采用了无状态的 代理层,对于 客户端 来说,一切都是透明的。
- 优点
实现了上层 Proxy
和底层 Redis
的 高可用,数据分片 和 自动平衡,提供 命令行接口 和 RESTful API
,提供 监控 和 管理 界面,可以动态 添加 和 删除 Redis
节点。
- 缺点
部署架构 和 配置 复杂,不支持 跨机房 和 多租户,不支持 鉴权管理。
1.3. 查询路由方案
客户端随机地 请求任意一个 Redis
实例,然后由 Redis
将请求 转发 给 正确 的 Redis
节点。Redis Cluster
实现了一种 混合形式 的 查询路由,但并不是 直接 将请求从一个 Redis
节点 转发 到另一个 Redis
节点,而是在 客户端 的帮助下直接 重定向( redirected
)到正确的 Redis
节点。
- 优点
无中心节点,数据按照 槽 存储分布在多个 Redis
实例上,可以平滑的进行节点 扩容/缩容,支持 高可用 和 自动故障转移,运维成本低。
- 缺点
严重依赖 Redis-trib
工具,缺乏 监控管理,需要依赖 Smart Client
(维护连接,缓存路由表,MultiOp
和 Pipeline
支持)。Failover
节点的 检测过慢,不如 中心节点 ZooKeeper
及时。Gossip
消息具有一定开销。无法根据统计区分 冷热数据。
2. 数据分布
2.1. 数据分布理论
分布式数据库 首先要解决把 整个数据集 按照 分区规则 映射到 多个节点 的问题,即把 数据集 划分到 多个节点 上,每个节点负责 整体数据 的一个 子集。
数据分布通常有 哈希分区 和 顺序分区 两种方式,对比如下:
哈希分区 | 离散程度好,数据分布与业务无关,无法顺序访问 | Redis Cluster,Cassandra,Dynamo |
顺序分区 | 离散程度易倾斜,数据分布与业务相关,可以顺序访问 | BigTable,HBase,Hypertable |
由于 Redis Cluster
采用 哈希分区规则,这里重点讨论 哈希分区。常见的 哈希分区 规则有几种,下面分别介绍:
2.1.1. 节点取余分区
使用特定的数据,如 Redis
的 键 或 用户 ID
,再根据 节点数量 N
使用公式:hash(key)% N
计算出 哈希值,用来决定数据 映射 到哪一个节点上。
- 优点
这种方式的突出优点是 简单性,常用于 数据库 的 分库分表规则。一般采用 预分区 的方式,提前根据 数据量 规划好 分区数,比如划分为 512
或 1024
张表,保证可支撑未来一段时间的 数据容量,再根据 负载情况 将 表 迁移到其他 数据库 中。扩容时通常采用 翻倍扩容,避免 数据映射 全部被 打乱,导致 全量迁移 的情况。
- 缺点
当 节点数量 变化时,如 扩容 或 收缩 节点,数据节点 映射关系 需要重新计算,会导致数据的 重新迁移。
2.1.2. 一致性哈希分区
一致性哈希 可以很好的解决 稳定性问题,可以将所有的 存储节点 排列在 收尾相接 的 Hash
环上,每个 key
在计算 Hash
后会 顺时针 找到 临接 的 存储节点 存放。而当有节点 加入 或 退出 时,仅影响该节点在 Hash
环上 顺时针相邻 的 后续节点。
- 优点
加入 和 删除 节点只影响 哈希环 中 顺时针方向 的 相邻的节点,对其他节点无影响。
- 缺点
加减节点 会造成 哈希环 中部分数据 无法命中。当使用 少量节点 时,节点变化 将大范围影响 哈希环 中 数据映射,不适合 少量数据节点 的分布式方案。普通 的 一致性哈希分区 在增减节点时需要 增加一倍 或 减去一半 节点才能保证 数据 和 负载的均衡。
注意:因为 一致性哈希分区 的这些缺点,一些分布式系统采用 虚拟槽 对 一致性哈希 进行改进,比如
Dynamo
系统。
2.1.3. 虚拟槽分区
虚拟槽分区 巧妙地使用了 哈希空间,使用 分散度良好 的 哈希函数 把所有数据 映射 到一个 固定范围 的 整数集合 中,整数定义为 槽(slot
)。这个范围一般 远远大于 节点数,比如 Redis Cluster
槽范围是 0 ~ 16383
。槽 是集群内 数据管理 和 迁移 的 基本单位。采用 大范围槽 的主要目的是为了方便 数据拆分 和 集群扩展。每个节点会负责 一定数量的槽,如图所示:
当前集群有 5
个节点,每个节点平均大约负责 3276
个 槽。由于采用 高质量 的 哈希算法,每个槽所映射的数据通常比较 均匀,将数据平均划分到 5
个节点进行 数据分区。Redis Cluster
就是采用 虚拟槽分区。
-
节点1: 包含
0
到3276
号哈希槽。 -
节点2:包含
3277
到6553
号哈希槽。 -
节点3:包含
6554
到9830
号哈希槽。 -
节点4:包含
9831
到13107
号哈希槽。 -
节点5:包含
13108
到16383
号哈希槽。
这种结构很容易 添加 或者 删除 节点。如果 增加 一个节点 6
,就需要从节点 1 ~ 5
获得部分 槽 分配到节点 6
上。如果想 移除 节点 1
,需要将节点 1
中的 槽 移到节点 2 ~ 5
上,然后将 没有任何槽 的节点 1
从集群中 移除 即可。
由于从一个节点将 哈希槽 移动到另一个节点并不会 停止服务,所以无论 添加删除 或者 改变 某个节点的 哈希槽的数量 都不会造成 集群不可用 的状态.
2.2. Redis的数据分区
Redis Cluster
采用 虚拟槽分区,所有的 键 根据 哈希函数 映射到 0~16383
整数槽内,计算公式:slot = CRC16(key)& 16383
。每个节点负责维护一部分槽以及槽所映射的 键值数据,如图所示:
2.2.1. Redis虚拟槽分区的特点
-
解耦 数据 和 节点 之间的关系,简化了节点 扩容 和 收缩 难度。
-
节点自身 维护槽的 映射关系,不需要 客户端 或者 代理服务 维护 槽分区元数据。
-
支持 节点、槽、键 之间的 映射查询,用于 数据路由、在线伸缩 等场景。
2.3. Redis集群的功能限制
Redis
集群相对 单机 在功能上存在一些限制,需要 开发人员 提前了解,在使用时做好规避。
-
key
批量操作 支持有限。
类似 mset
、mget
操作,目前只支持对具有相同 slot
值的 key
执行 批量操作。对于 映射为不同 slot
值的 key
由于执行 mget
、mget
等操作可能存在于多个节点上,因此不被支持。
-
key
事务操作 支持有限。
只支持 多 key
在 同一节点上 的 事务操作,当多个 key
分布在 不同 的节点上时 无法 使用事务功能。
-
key
作为 数据分区 的最小粒度
不能将一个 大的键值 对象如 hash
、list
等映射到 不同的节点。
- 不支持 多数据库空间
单机 下的 Redis
可以支持 16
个数据库(db0 ~ db15
),集群模式 下只能使用 一个 数据库空间,即 db0
。
- 复制结构 只支持一层
从节点 只能复制 主节点,不支持 嵌套树状复制 结构。
3. Redis集群搭建
Redis-Cluster
是 Redis
官方的一个 高可用 解决方案,Cluster
中的 Redis
共有 2^14(16384)
个 slot
槽。创建 Cluster
后,槽 会 平均分配 到每个 Redis
节点上。
下面介绍一下本机启动 6
个 Redis
的 集群服务,并使用 redis-trib.rb
创建 3主3从 的 集群。搭建集群工作需要以下三个步骤:
3.1. 准备节点
Redis
集群一般由 多个节点 组成,节点数量至少为 6
个,才能保证组成 完整高可用 的集群。每个节点需要 开启配置 cluster-enabled yes
,让 Redis
运行在 集群模式 下。
Redis
集群的节点规划如下:
redis-6379 | 6379 | 主节点 | --- |
redis-6389 | 6389 | 从节点 | redis-6379 |
redis-6380 | 6380 | 主节点 | --- |
redis-6390 | 6390 | 从节点 | redis-6380 |
redis-6381 | 6381 | 主节点 | --- |
redis-6391 | 6391 | 从节点 | redis-6381 |
注意:建议为集群内 所有节点 统一目录,一般划分三个目录:
conf
、data
、log
,分别存放 配置、数据 和 日志 相关文件。把6
个节点配置统一放在conf
目录下。
3.1.1. 创建redis各实例目录
$ sudo mkdir -p /usr/local/redis-cluster
$ cd /usr/local/redis-cluster
$ sudo mkdir conf data log
$ sudo mkdir -p data/redis-6379 data/redis-6389 data/redis-6380 data/redis-6390 data/redis-6381 data/redis-6391
复制代码
3.1.2. redis配置文件管理
根据以下 模板 配置各个实例的 redis.conf
,以下只是搭建集群需要的 基本配置,可能需要根据实际情况做修改。
# redis后台运行
daemonize yes
# 绑定的主机端口
bind 127.0.0.1
# 数据存放目录
dir /usr/local/redis-cluster/data/redis-6379
# 进程文件
pidfile /var/run/redis-cluster/${自定义}.pid
# 日志文件
logfile /usr/local/redis-cluster/log/${自定义}.log
# 端口号
port 6379
# 开启集群模式,把注释#去掉
cluster-enabled yes
# 集群的配置,配置文件首次启动自动生成
cluster-config-file /usr/local/redis-cluster/conf/${自定义}.conf
# 请求超时,设置10秒
cluster-node-timeout 10000
# aof日志开启,有需要就开启,它会每次写操作都记录一条日志
appendonly yes
复制代码
- redis-6379.conf
daemonize yes
bind 127.0.0.1
dir /usr/local/redis-cluster/data/redis-6379
pidfile /var/run/redis-cluster/redis-6379.pid
logfile /usr/local/redis-cluster/log/redis-6379.log
port 6379
cluster-enabled yes
cluster-config-file /usr/local/redis-cluster/conf/node-6379.conf
cluster-node-timeout 10000
appendonly yes
复制代码
- redis-6389.conf
daemonize yes
bind 127.0.0.1
dir /usr/local/redis-cluster/data/redis-6389
pidfile /var/run/redis-cluster/redis-6389.pid
logfile /usr/local/redis-cluster/log/redis-6389.log
port 6389
cluster-enabled yes
cluster-config-file /usr/local/redis-cluster/conf/node-6389.conf
cluster-node-timeout 10000
appendonly yes
复制代码
- redis-6380.conf
daemonize yes
bind 127.0.0.1
dir /usr/local/redis-cluster/data/redis-6380
pidfile /var/run/redis-cluster/redis-6380.pid
logfile /usr/local/redis-cluster/log/redis-6380.log
port 6380
cluster-enabled yes
cluster-config-file /usr/local/redis-cluster/conf/node-6380.conf
cluster-node-timeout 10000
appendonly yes
复制代码
- redis-6390.conf
daemonize yes
bind 127.0.0.1
dir /usr/local/redis-cluster/data/redis-6390
pidfile /var/run/redis-cluster/redis-6390.pid
logfile /usr/local/redis-cluster/log/redis-6390.log
port 6390
cluster-enabled yes
cluster-config-file /usr/local/redis-cluster/conf/node-6390.conf
cluster-node-timeout 10000
appendonly yes
复制代码
- redis-6381.conf
daemonize yes
bind 127.0.0.1
dir /usr/local/redis-cluster/data/redis-6381
pidfile /var/run/redis-cluster/redis-6381.pid
logfile /usr/local/redis-cluster/log/redis-6381.log
port 6381
cluster-enabled yes
cluster-config-file /usr/local/redis-cluster/conf/node-6381.conf
cluster-node-timeout 10000
appendonly yes
复制代码
- redis-6391.conf
daemonize yes
bind 127.0.0.1
dir /usr/local/redis-cluster/data/redis-6391
pidfile /var/run/redis-cluster/redis-6391.pid
logfile /usr/local/redis-cluster/log/redis-6391.log
port 6391
cluster-enabled yes
cluster-config-file /usr/local/redis-cluster/conf/node-6391.conf
cluster-node-timeout 10000
appendonly yes
复制代码
3.2. 环境准备
3.2.1. 安装Ruby环境
$ sudo brew install ruby
复制代码
3.2.2. 准备rubygem redis依赖
$ sudo gem install redis
Password:
Fetching: redis-4.0.2.gem (100%)
Successfully installed redis-4.0.2
Parsing documentation for redis-4.0.2
Installing ri documentation for redis-4.0.2
Done installing documentation for redis after 1 seconds
1 gem installed
复制代码
3.2.3. 拷贝redis-trib.rb到集群根目录
redis-trib.rb
是 redis
官方推出的管理 redis
集群 的工具,集成在 redis
的源码 src
目录下,将基于 redis
提供的 集群命令 封装成 简单、便捷、实用 的 操作工具。
$ sudo cp /usr/local/redis-4.0.11/src/redis-trib.rb /usr/local/redis-cluster
复制代码
查看 redis-trib.rb
命令环境是否正确,输出如下:
$ ./redis-trib.rb
Usage: redis-trib <command> <options> <arguments ...>
create host1:port1 ... hostN:portN
--replicas <arg>
check host:port
info host:port
fix host:port
--timeout <arg>
reshard host:port
--from <arg>
--to <arg>
--slots <arg>
--yes
--timeout <arg>
--pipeline <arg>
rebalance host:port
--weight <arg>
--auto-weights
--use-empty-masters
--timeout <arg>
--simulate
--pipeline <arg>
--threshold <arg>
add-node new_host:new_port existing_host:existing_port
--slave
--master-id <arg>
del-node host:port node_id
set-timeout host:port milliseconds
call host:port command arg arg .. arg
import host:port
--from <arg>
--copy
--replace
help (show this help)
For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.
复制代码
redis-trib.rb
是 redis
作者用 ruby
完成的。redis-trib.rb
命令行工具 的具体功能如下:
create | 创建集群 |
check | 检查集群 |
info | 查看集群信息 |
fix | 修复集群 |
reshard | 在线迁移slot |
rebalance | 平衡集群节点slot数量 |
add-node | 将新节点加入集群 |
del-node | 从集群中删除节点 |
set-timeout | 设置集群节点间心跳连接的超时时间 |
call | 在集群全部节点上执行命令 |
import | 将外部redis数据导入集群 |
3.3. 安装集群
3.3.1. 启动redis服务节点
运行如下命令启动 6
台 redis
节点:
sudo redis-server conf/redis-6379.conf
sudo redis-server conf/redis-6389.conf
sudo redis-server conf/redis-6380.conf
sudo redis-server conf/redis-6390.conf
sudo redis-server conf/redis-6381.conf
sudo redis-server conf/redis-6391.conf
复制代码
启动完成后,redis
以集群模式启动,查看各个 redis
节点的进程状态:
$ ps -ef | grep redis-server
0 1908 1 0 4:59下午 ?? 0:00.01 redis-server *:6379 [cluster]
0 1911 1 0 4:59下午 ?? 0:00.01 redis-server *:6389 [cluster]
0 1914 1 0 4:59下午 ?? 0:00.01 redis-server *:6380 [cluster]
0 1917 1 0 4:59下午 ?? 0:00.01 redis-server *:6390 [cluster]
0 1920 1 0 4:59下午 ?? 0:00.01 redis-server *:6381 [cluster]
0 1923 1 0 4:59下午 ?? 0:00.01 redis-server *:6391 [cluster]
复制代码
在每个 redis
节点的 redis.conf
文件中,我们都配置了 cluster-config-file
的文件路径,集群启动时,conf
目录会新生成 集群 节点配置文件。查看文件列表如下:
$ tree -L 3 .
.
├── appendonly.aof
├── conf
│ ├── node-6379.conf
│ ├── node-6380.conf
│ ├── node-6381.conf
│ ├── node-6389.conf
│ ├── node-6390.conf
│ ├── node-6391.conf
│ ├── redis-6379.conf
│ ├── redis-6380.conf
│ ├── redis-6381.conf
│ ├── redis-6389.conf
│ ├── redis-6390.conf
│ └── redis-6391.conf
├── data
│ ├── redis-6379
│ ├── redis-6380
│ ├── redis-6381
│ ├── redis-6389
│ ├── redis-6390
│ └── redis-6391
├── log
│ ├── redis-6379.log
│ ├── redis-6380.log
│ ├── redis-6381.log
│ ├── redis-6389.log
│ ├── redis-6390.log
│ └── redis-6391.log
└── redis-trib.rb
9 directories, 20 files
复制代码
3.3.2. redis-trib关联集群节点
按照 从主到从 的方式 从左到右 依次排列 6
个 redis
节点。
$ sudo ./redis-trib.rb create --replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6389 127.0.0.1:6390 127.0.0.1:6391
复制代码
集群创建后,redis-trib
会先将 16384
个 哈希槽 分配到 3
个 主节点,即 redis-6379
,redis-6380
和 redis-6381
。然后将各个 从节点 指向 主节点,进行 数据同步。
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
127.0.0.1:6379
127.0.0.1:6380
127.0.0.1:6381
Adding replica 127.0.0.1:6390 to 127.0.0.1:6379
Adding replica 127.0.0.1:6391 to 127.0.0.1:6380
Adding replica 127.0.0.1:6389 to 127.0.0.1:6381
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: ad4b9ffceba062492ed67ab336657426f55874b7 127.0.0.1:6379
slots:0-5460 (5461 slots) master
M: df23c6cad0654ba83f0422e352a81ecee822702e 127.0.0.1:6380
slots:5461-10922 (5462 slots) master
M: ab9da92d37125f24fe60f1f33688b4f8644612ee 127.0.0.1:6381
slots:10923-16383 (5461 slots) master
S: 25cfa11a2b4666021da5380ff332b80dbda97208 127.0.0.1:6389
replicates ad4b9ffceba062492ed67ab336657426f55874b7
S: 48e0a4b539867e01c66172415d94d748933be173 127.0.0.1:6390
replicates df23c6cad0654ba83f0422e352a81ecee822702e
S: d881142a8307f89ba51835734b27cb309a0fe855 127.0.0.1:6391
replicates ab9da92d37125f24fe60f1f33688b4f8644612ee
复制代码
然后输入 yes
,redis-trib.rb
开始执行 节点握手 和 槽分配 操作,输出如下:
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 127.0.0.1:6379)
M: ad4b9ffceba062492ed67ab336657426f55874b7 127.0.0.1:6379
slots:0-5460 (5461 slots) master
1 additional replica(s)
M: ab9da92d37125f24fe60f1f33688b4f8644612ee 127.0.0.1:6381
slots:10923-16383 (5461 slots) master
1 additional replica(s)
S: 48e0a4b539867e01c66172415d94d748933be173 127.0.0.1:6390
slots: (0 slots) slave
replicates df23c6cad0654ba83f0422e352a81ecee822702e
S: d881142a8307f89ba51835734b27cb309a0fe855 127.0.0.1:6391
slots: (0 slots) slave
replicates ab9da92d37125f24fe60f1f33688b4f8644612ee
M: df23c6cad0654ba83f0422e352a81ecee822702e 127.0.0.1:6380
slots:5461-10922 (5462 slots) master
1 additional replica(s)
S: 25cfa11a2b4666021da5380ff332b80dbda97208 127.0.0.1:6389
slots: (0 slots) slave
replicates ad4b9ffceba062492ed67ab336657426f55874b7
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
复制代码
执行 集群检查,检查各个 redis
节点占用的 哈希槽(slot
)的个数以及 slot
覆盖率。16384
个槽位中,主节点 redis-6379
、redis-6380
和 redis-6381
分别占用了 5461
、5461
和 5462
个槽位。
3.3.3. redis主节点的日志
可以发现,通过 BGSAVE
命令,从节点 redis-6389
在 后台 异步地从 主节点 redis-6379
同步数据。
$ cat log/redis-6379.log
1907:C 05 Sep 16:59:52.960 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1907:C 05 Sep 16:59:52.961 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=1907, just started
1907:C 05 Sep 16:59:52.961 # Configuration loaded
1908:M 05 Sep 16:59:52.964 * Increased maximum number of open files to 10032 (it was originally set to 256).
1908:M 05 Sep 16:59:52.965 * No cluster configuration found, I'm ad4b9ffceba062492ed67ab336657426f55874b7
1908:M 05 Sep 16:59:52.967 * Running mode=cluster, port=6379.
1908:M 05 Sep 16:59:52.967 # Server initialized
1908:M 05 Sep 16:59:52.967 * Ready to accept connections
1908:M 05 Sep 17:01:17.782 # configEpoch set to 1 via CLUSTER SET-CONFIG-EPOCH
1908:M 05 Sep 17:01:17.812 # IP address for this node updated to 127.0.0.1
1908:M 05 Sep 17:01:22.740 # Cluster state changed: ok
1908:M 05 Sep 17:01:23.681 * Slave 127.0.0.1:6389 asks for synchronization
1908:M 05 Sep 17:01:23.681 * Partial resynchronization not accepted: Replication ID mismatch (Slave asked for '4c5afe96cac51cde56039f96383ea7217ef2af41', my replication IDs are '037b661bf48c80c577d1fa937ba55367a3692921' and '0000000000000000000000000000000000000000')
1908:M 05 Sep 17:01:23.681 * Starting BGSAVE for SYNC with target: disk
1908:M 05 Sep 17:01:23.682 * Background saving started by pid 1952
1952:C 05 Sep 17:01:23.683 * DB saved on disk
1908:M 05 Sep 17:01:23.749 * Background saving terminated with success
1908:M 05 Sep 17:01:23.752 * Synchronization with slave 127.0.0.1:6389 succeeded
复制代码
3.3.4. redis集群完整性检测
使用 redis-trib.rb check
命令检测之前创建的 两个集群 是否成功,check
命令只需要给出集群中 任意一个节点地址 就可以完成 整个集群 的 检查工作,命令如下:
$ ./redis-trib.rb check 127.0.0.1:6379
复制代码
当最后输出如下信息,提示集群 所有的槽 都已分配到节点:
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
复制代码
小结
本文介绍了 Redis
集群解决方案,数据分布 和 集群搭建。集群方案包括 客户端分区 方案,代理分区 方案 和 查询路由 方案。数据分布 部分简单地对 节点取余 分区,一致性哈希 分区以及 虚拟槽 分区进行了阐述和对比。最后对使用 Redis-trib
搭建了一个 三主三从 的 虚拟槽 集群示例。
From:
https://juejin.im/post/5b76e732f265da4376203849