- Redis 在做 master-slave 的高可用方案时, 如果 master 宕机了, Redis 本身没有实现自动主备切换的特性, Sentinel 可以做到这一点
- 当 Sentinel 集群中多数 (如果节点数为 3, 这个阈值就应该是 2) Sentinel 都认为当前 master 故障
- 才会进行主备切换, 投票将剩下的备用节点中的其中一台提升为 master 节点, 并自动修改 redis.conf 配置文件, 使其余备用节点响应新的 master
配置哨兵
- 占用 7501 ~ 7507 7 个端口
- 其中 7501 ~ 7504 4 个端口为 Redis Instance 集群
- 包含 1 主 3 从, 7505 ~ 7507 3 个端口为 Redis
- Sentinel 集群 包含 1 主 2 从.
- 在该目录下新建 7 个文件夹:
mkdir /usr/local/redis-5.0.2-sentinel/{7501,7502,7503,7504,7505,7506,7507}
./redis-5.0.2-sentinel/
├── 7501
│ └── redis-7501.conf
├── 7502
│ └── redis-7502.conf
├── 7503
│ └── redis-7503.conf
├── 7504
│ └── redis-7504.conf
├── 7505
│ └── sentinel-7505.conf
├── 7506
│ └── sentinel-7506.conf
└── 7507
└── sentinel-7507.conf
复制配置文件
/usr/local/redis-5.0.2-sentinel/7501/redis-7501.conf,
/usr/local/redis-5.0.2-sentinel/7502/redis-7502.conf,
/usr/local/redis-5.0.2-sentinel/7503/redis-7503.conf,
/usr/local/redis-5.0.2-sentinel/7504/redis-7504.conf:
# 依次从之前安装的 Redis 目录下拷贝 redis.conf 至
cp ./redis-5.0.2/redis.conf ./redis-5.0.2-sentinel/7501/redis-7501.conf
cp ./redis-5.0.2/redis.conf ./redis-5.0.2-sentinel/7502/redis-7502.conf
cp ./redis-5.0.2/redis.conf ./redis-5.0.2-sentinel/7503/redis-7503.conf
cp ./redis-5.0.2/redis.conf ./redis-5.0.2-sentinel/7504/redis-7504.conf
依次更改配置文件, 主要涉及端口, 日志文件, 工作目录等配置, 以 7501 举例:
port=7501
# 支持后台 (守护进程) 的方式运行
daemonize=yes
pidfile=/var/run/redis_7501.pid
# 需要注意的是: 如果设置成 ./redis-7501.log, 则实际日志文件的存放位置是相对路径, 相对的是你执行 redis-server <the-path-of-config-file> 命令的路径
logfile="/usr/local/redis-5.0.2-sentinel/7501/redis-7501.log"
dir="/usr/local/redis-5.0.2-sentinel/7501"
# 当前实例的密钥
requirepass=redis-cluster-pass
# master 节点的密钥, 由于要执行主备切换, requirepass 和 masterauth 应该一致
masterauth=redis-cluster-pass
appendonly=yes
分别启动 4 个 Redis Instance
redis-server /usr/local/redis-5.0.2-sentinel/7501/redis-7501.conf
redis-server /usr/local/redis-5.0.2-sentinel/7502/redis-7502.conf
redis-server /usr/local/redis-5.0.2-sentinel/7503/redis-7503.conf
redis-server /usr/local/redis-5.0.2-sentinel/7504/redis-7504.conf
设置主从关系
- 也可以在
redis-<port>.conf
中通过 replicaof 配置:
[root@VM-0-9-centos ~]# redis-cli -h 127.0.0.1 -p 7502
127.0.0.1:7502> auth redis-cluster-pass
OK
127.0.0.1:7502> SLAVEOF 127.0.0.1 7501
OK
127.0.0.1:7502> exit
[root@VM-0-9-centos ~]# redis-cli -h 127.0.0.1 -p 7503
127.0.0.1:7503> auth redis-cluster-pass
OK
127.0.0.1:7503> SLAVEOF 127.0.0.1 7501
OK
127.0.0.1:7503> exit
[root@VM-0-9-centos ~]# redis-cli -h 127.0.0.1 -p 7504
127.0.0.1:7504> auth redis-cluster-pass
OK
127.0.0.1:7504> SLAVEOF 127.0.0.1 7501
OK
127.0.0.1:7504> exit
[root@VM-0-9-centos ~]#
主从conf
# 修改为后台启动
daemonize yes
# 修改连接
【坑】: bind 127.0.0.1(删掉) ; 改为:bind 0.0.0.0
# master节点的ip和端口号
slaveof <masterip> <masterport>
# master节点的登录密码,若master节点没有密码,则不需要
masterauth <master-password>
cli 登陆 master 节点, 查看 Redis Instance 状态:
[root@VM-0-9-centos redis-5.0.2-sentinel]# redis-cli -h 127.0.0.1 -p 7501
127.0.0.1:7501> auth redis-cluster-pass
OK
127.0.0.1:7501> info replication
# Replication
role:master
connected_slaves:3
slave0:ip=127.0.0.1,port=7502,state=online,offset=644,lag=1
slave1:ip=127.0.0.1,port=7503,state=online,offset=644,lag=1
slave2:ip=127.0.0.1,port=7504,state=online,offset=644,lag=1
master_replid:6d8140a2963cbc57a7799c2300d9c08f125d4924
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:644
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:644
127.0.0.1:7501>
Redis Sentinel 配置
- 将之前安装文件夹下的
sentinel.conf
一次拷贝到/usr/local/redis-5.0.2-sentinel/7505/sentinel-7505.conf, /usr/local/redis-5.0.2-sentinel/7506/sentinel-7506.conf, /usr/local/redis-5.0.2-sentinel/7507/sentinel-7507.conf:
cp ./redis-5.0.2/sentinel.conf ./redis-5.0.2-sentinel/7505/sentinel-7505.conf
cp ./redis-5.0.2/sentinel.conf ./redis-5.0.2-sentinel/7506/sentinel-7506.conf
cp ./redis-5.0.2/sentinel.conf ./redis-5.0.2-sentinel/7507/sentinel-7507.conf
修改配置文件, 以 7505 为例
# 需要在 Sentinel 的配置文件中指定 Sentinel 的密钥
# 如果不做这个操作, 在 SpringBoot 集成的时候, Redis Instance 有密钥保护, 而 Redis Sentinel 没有:
# application.yml 如果不设置密钥, 会报错: NOAUTH Authentication required.
# application.yml 设置了密钥又囧报: ERR Client sent AUTH, but no password is set
requirepass="redis-cluster-pass"
port=7505
daemonize=yes
pidfile=/var/run/redis-sentinel-7505.pid
logfile="/usr/local/redis-5.0.2-sentinel/7505/sentinel-7505.log"
dir="/usr/local/redis-5.0.2-sentinel/7505"
# 默认将主节点设置为 7501,
# redis-5.0.2-master 是别名;
# 2 表示只要有 2 个 Sentinel 赞同, 就执行故障转移 (主备切换), 这个数字不能大于 Sentinel 的个数
sentinel=monitor redis-5.0.2-master 127.0.0.1 7501 2
# 主节点的访问信息
sentinel=auth-pass redis-5.0.2-master redis-cluster-pass
启动 3 个哨兵
[root@VM-0-9-centos redis-5.0.2-sentinel]# redis-sentinel 7505/sentinel-7505.conf
[root@VM-0-9-centos redis-5.0.2-sentinel]# redis-sentinel 7506/sentinel-7506.conf
[root@VM-0-9-centos redis-5.0.2-sentinel]# redis-sentinel 7507/sentinel-7507.conf
[root@VM-0-9-centos redis-5.0.2-sentinel]# ps -ef | grep redis
root 6625 1 0 01:09 ? 00:00:00 redis-server 0.0.0.0:7501
root 6644 1 0 01:09 ? 00:00:00 redis-server 0.0.0.0:7502
root 6664 1 0 01:09 ? 00:00:00 redis-server 0.0.0.0:7503
root 6677 1 0 01:09 ? 00:00:00 redis-server 0.0.0.0:7504
root 7694 1 0 01:15 ? 00:00:00 redis-sentinel *:7505 [sentinel]
root 7714 1 0 01:15 ? 00:00:00 redis-sentinel *:7506 [sentinel]
root 7744 1 0 01:15 ? 00:00:00 redis-sentinel *:7507 [sentinel]
root 7841 27334 0 01:16 pts/0 00:00:00 grep --color=auto redis
[root@VM-0-9-centos redis-5.0.2-sentinel]#
测试
- 登陆主节点, 设置一个 KeyValue, 可以看到从节点也有对应的记录 (默认主节点可读写, 从节点只可读):
[root@VM-0-9-centos local]# redis-cli -h 127.0.0.1 -p 7501
127.0.0.1:7501> auth redis-cluster-pass
OK
127.0.0.1:7501> set foo bar
OK
127.0.0.1:7501> exit
[root@VM-0-9-centos local]# redis-cli -h 127.0.0.1 -p 7503
127.0.0.1:7503> get foo
(error) NOAUTH Authentication required.
127.0.0.1:7503> auth redis-cluster-pass
OK
127.0.0.1:7503> get foo
"bar"
127.0.0.1:7503> exit
kill 掉主节点试试
[root@VM-0-9-centos ~]# ps -ef | grep redis
root 6625 1 0 01:09 ? 00:00:23 redis-server 0.0.0.0:7501
root 6644 1 0 01:09 ? 00:00:22 redis-server 0.0.0.0:7502
root 6664 1 0 01:09 ? 00:00:22 redis-server 0.0.0.0:7503
root 6677 1 0 01:09 ? 00:00:22 redis-server 0.0.0.0:7504
root 7694 1 0 01:15 ? 00:00:42 redis-sentinel *:7505 [sentinel]
root 7714 1 0 01:15 ? 00:00:42 redis-sentinel *:7506 [sentinel]
root 7744 1 0 01:15 ? 00:00:42 redis-sentinel *:7507 [sentinel]
root 29385 29280 0 09:43 pts/0 00:00:00 grep --color=auto redis
[root@VM-0-9-centos ~]# kill -9 6625
[root@VM-0-9-centos ~]# ps -ef | grep redis
root 6644 1 0 01:09 ? 00:00:22 redis-server 0.0.0.0:7502
root 6664 1 0 01:09 ? 00:00:22 redis-server 0.0.0.0:7503
root 6677 1 0 01:09 ? 00:00:22 redis-server 0.0.0.0:7504
root 7694 1 0 01:15 ? 00:00:42 redis-sentinel *:7505 [sentinel]
root 7714 1 0 01:15 ? 00:00:42 redis-sentinel *:7506 [sentinel]
root 7744 1 0 01:15 ? 00:00:42 redis-sentinel *:7507 [sentinel]
root 30638 29280 0 09:50 pts/0 00:00:00 grep --color=auto redis
[root@VM-0-9-centos ~]# redis-cli -h 127.0.0.1 -p 7502
127.0.0.1:7502> auth redis-cluster-pass
OK
127.0.0.1:7502> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:7504
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:6511835
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:c60c6880cb9054c543a8472adf73432df74bf20b
master_replid2:aa1551df9635dcc155e510aaa6712cda6a7e7bff
master_repl_offset:6511835
second_repl_offset:6506247
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:5463260
repl_backlog_histlen:1048576
127.0.0.1:7502>
spring:
redis:
#Redis 集群模式
#password: larry123456
cluster:
nodes: 192.168.127.101:6379,192.168.127.102:6379,192.168.127.103:6379
#Redis 哨兵模式
#password: larry123456
#sentinel:
# master: larry-master
# nodes: 192.168.127.130:26379,192.168.127.128:26379,192.168.127.129:26379
timeout: 10000 #连接超时问题
#Redis 单机模式
# host: 192.168.127.128
# port: 6379
# password: larry123456
- 看从节点信息
127.0.0.1:6379> info replication
redis集群
数据分片
- Redis 集群使用数据分片(sharding)而非一致性哈希(consistency hashing)来实现: 一个 Redis 集群包含 16384 个哈希槽(hash slot)
- 数据库中的每个键都属于这 16384 个哈希槽的其中一个, 集群使用公式
CRC16(key) % 16384
来计算键 key 属于哪个槽 - 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。
- 集群中的每个节点负责处理一部分哈希槽。
- 举个例子, 一个集群可以有三个哈希槽, 其中:
- 节点 A 负责处理 0 号至 5500 号哈希槽。
- 节点 B 负责处理 5501 号至 11000 号哈希槽。
- 节点 C 负责处理 11001 号至 16384 号哈希槽。
- 按照Redis Cluster的哈希槽算法,
CRC16('my_name')%16384 = 2412
那么这个key就被分配到了节点A上 - 同样的,当连接(A,B,C)的任意一个节点想获取my_name这个key,都会转到节点A上
- 如果用户将新节点 D 添加到集群中, 那么集群只需要将节点 A 、B 、 C 中的某些槽移动到节点 D 就可以了。
- 增加一个D节点的结果可能如下:
- 节点A覆盖1365-5460
- 节点B覆盖6827-10922
- 节点C覆盖12288-16383
- 节点D覆盖0-1364,5461-6826,10923-1228
搭建Redis集群
- 要让集群正常工作至少需要3个主节点,在这里我们要创建6个redis节点
- 其中三个为主节点,三个为从节点,对应的redis节点的ip和端口对应关系如下
127.0.0.1:7000
127.0.0.1:7001
127.0.0.1:7002
127.0.0.1:7003
127.0.0.1:7004
127.0.0.1:7005
# 解压安装
tar zxvf redis-3.2.9.tar.gz
cd redis-3.2.9
make && make PREFIX=/usr/local/redis install
# 这里如果失败的自行yum安装gcc和tcl
yum install gcc
yum install tcl
# 创建目录
cd /usr/local/redis
mkdir cluster
cd cluster
mkdir 7000 7001 7002 7003 7004 7005
# 复制和修改配置文件
# 将redis目录下的配置文件复制到对应端口文件夹下,6个文件夹都要复制一份
cp redis-3.2.9/redis.conf /usr/local/redis/cluster/7000
# 修改配置文件redis.conf,将下面的选项修改
redis.conf,将下面的选项修改
# 修改为后台启动
daemonize yes
# 修改端口号
port 8001
# 指定数据文件存储位置
dir /usr/local/redis-app/8001/
# 开启集群模式
cluster-enabled yes
# 集群节点信息文件配置
cluster-config-file nodes-8001.conf
# 集群节点超时间
cluster-node-timeout 15000
# 去掉bind绑定地址
# bind 127.0.0.1 -::1 (这里没写错就是家#注释掉bind配置)
# 关闭保护模式
protected-mode no
# 开启aof模式持久化
appendonly yes
# 设置连接Redis需要密码123(选配)
requirepass 123456
# 设置Redis节点与节点之间访问需要密码123(选配)
masterauth 123456
# 端口号
port 7000
# 后台启动
daemonize yes
# 开启集群
cluster-enabled yes
#集群节点配置文件
cluster-config-file nodes-7000.conf
# 集群连接超时时间
cluster-node-timeout 5000
# 进程pid的文件位置
pidfile /var/run/redis-7000.pid
# 开启aof
appendonly yes
# aof文件路径
appendfilename "appendonly-7005.aof"
# rdb文件路径
dbfilename dump-7000.rdb
创建脚本
- 修改完毕后,逐个启动Reids服务,启动成功后我们可以从
ps -ef
中看到启动的Redis进程于普通的Redis进程不同之处在于后面的[cluster]
表示当前进程是集群模式启动的
# 创建启动脚本
# 在/usr/local/redis目录下创建一个start.sh
#!/bin/bash
bin/redis-server cluster/7000/redis.conf
bin/redis-server cluster/7001/redis.conf
bin/redis-server cluster/7002/redis.conf
bin/redis-server cluster/7003/redis.conf
bin/redis-server cluster/7004/redis.conf
bin/redis-server cluster/7005/redis.conf
- 在任意一台机器上执行如下命令,即可创建集群,执行如下命令后Redis会随机分配主从机器,并且在分配的时Redis是不会让主节点与从节点在同一台机器上的
# -a 密码认证,若没写密码无效带这个参数
# --cluster create 创建集群实例列表 IP:PORT IP:PORT IP:PORT
# --cluster-replicas 复制因子1(即每个主节点需1个从节点)
./bin/redis-cli -a 123456 --cluster create --cluster-replicas 1 192.168.100.101:8001 192.168.100.101:8002 192.168.100.102:8003 192.168.100.102:8004 192.168.100.103:8005 192.168.100.103:8006
; 执行命令后会让你确认配置,输入yes确认
- 集群验证
# -a 密码认证
# -c 连接集群
# -h 集群中任意一个Redis节点IP
# -p 集群中任意一个Redis节点端口
./bin/redis-cli -a 123456 -c -h 192.168.100.101 -p 8001
# 登录redis-cli后执行如下命令
CLUSTER INFO
redis.conf配置文件备注
# redis进程是否以守护进程的方式运行,yes为是,no为否(不以守护进程的方式运行会占用一个终端)。
daemonize no
# 指定redis进程的PID文件存放位置
pidfile /var/run/redis.pid
# redis进程的端口号
port 6379
#是否开启保护模式,默认开启。要是配置里没有指定bind和密码。开启该参数后,redis只会本地进行访问,拒绝外部访问。要是开启了密码和bind,可以开启。否则最好关闭设置为no。
protected-mode yes
# 绑定的主机地址
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