1. 目标
- 介绍Redis的主从部署、哨兵部署与多主集群部署模式
- 实现Redis主从与哨兵集群部署,以及多主集群模式的搭建配置
2. 脉络
- 介绍Redis的所有部署模式,特点与使用场景
- 完成Redis主从与哨兵集群部署
- 完成Redis多主集群模式部署
- 应用项目与集群的连接配置
3. 知行
3.1 Redis部署模式简介
Redis部署模式有多种, 包括单节点模式, 主从模式, 主从+哨兵模式, 以及集群模式。
- Redis单节点模式
安装简单,客户端直连, 操作简便; 不存在多节点数据同步或脑裂问题, 在开发环境中可直接采用单节点模式, 简化维护。如果应用服务规模比较小, 可以直接采用单节点单机部署。 - 主从模式
单节点模式部署主要存在两个问题: 数据备份和数据体量较大造成的性能降低,主从模式指的是使用一个redis实例作为主机,其余的实例作为备份机, 主节点负责读写, 从节点专门负责数据的读取。
主从同步机制: 从节点启动后, 主动向master发送SYNC命令, 主节点接收到SYNC命令后在后台保存快照, 将保存的快照文件和期间的缓存操作指令发送给从节点。
如果具有一定数据规模和性能要求, 可以采用主从模式, - 哨兵模式
主从模式下, 如果主节点出现故障,从节点因为没有主节点同步而中断, 需要人工进行故障处理。Sentinel哨兵模式是Redis推出的原生高可用解决方案, 主要包括两部分, Sentinel集群和Redis主从集群。
哨兵的功能主要是监控主从节点运行是否正常, 当master主节点出现故障时, 会自动将slave从节点转化为主节点。 要求较高的稳定性和高可用性, 可采用哨兵模式。
- 集群模式
主从和哨兵模式虽然解决了一定的性能和数据瓶颈, 但是对于大量的写操作场景, 还是不能满足, 如果对于从节点出现故障, 哨兵模式是不会对其进行故障转移, 因此在Redis3.0版本推出了cluster集群功能, 能够有效解决这些问题。
Cluster集群处理机制: 集群总共定义了16384个槽(slot),所有数据根据一致性哈希算法映射到某个槽中, 16384个槽根据Redis实例数量进行平均分配, 比如有A、B、C三个实例, 集群会将0-5460号槽分配给A,5461-10922号槽分配给B,10923-16383号槽分配给C。由于一致hash算法是一定的, 无论多少个redis实例,都可以将这16384个槽散列平均分配。Redis集群通过这种机制,来达到高性能和高可用性。
在大型项目应用中,具有海量数据规模, 要求较高的稳定性和扩展性, 可以采用集群模式。
3.2 Redis哨兵模式部署
3.2.1 部署规划
Redis主从部署:
20.20.50.26: Redis主节点, 端口6379
20.20.50.27: Redis从节点, 端口6379
20.20.50.28: Redis从节点, 端口6379
哨兵部署:
20.20.50.26: 哨兵节点一, 端口26379
20.20.50.27: 哨兵节点二, 端口26379
20.20.50.28: 哨兵节点三, 端口26379
3.2.2 Redis安装
先将Redis服务安装在三台节点上。
- 安装依赖组件
yum -y install tcl gcc
- 下载安装包
wget http://download.redis.io/releases/redis-4.0.2.tar.gz
- 解压
tar -xvf redis-4.0.2.tar.gz
- 编译安装
cd redis-4.0.2
make
make install
如果make 错误, 可以执行make distclean 清理, 再重新make编译。
安装成功, 可以看到以下提示:
Hint: It's a good idea to run 'make test' ;)
INSTALL install
INSTALL install
INSTALL install
INSTALL install
INSTALL install
- 配置
- 复制启动脚本
cp utils/redis_init_script /etc/init.d/redis_6379
- 复制配置文件
mkdir -p /etc/redis
cp redis.conf /etc/redis/6379.conf
- 修改配置文件:
vi /etc/redis/6379.conf
- 修改内容:
# 后台方式运行
daemonize yes
# PID文件保存位置
pidfile /var/run/redis_6379.pid
# Redis服务监听端口
port 6379
# 持久化文件存储路径, 如果没有此目录, 则创建: make -p /var/redis/6379
dir /var/redis/6379
# 绑定IP地址, 如果要远程连接, 需要绑定外部IP地址, 可以绑定多个, 以空格分隔
bind 127.0.0.1
- 设置开机启动
修改启动脚本:
vi /etc/init.d/redis_6379
在头部增加:
#!/bin/sh
# chkconfig: 2345 90 10
- 启动与关闭
/etc/init.d/redis_6379 start
/etc/init.d/redis_6379 stop
3.2.3 主从创建
这里20.20.50.26为主节点, 将20.20.50.27与20.20.50.28作为从节点, 纳入集群。
- 手动创建
- 进入20.20.50.27
执行以下命令:
[root@localhost local]# redis-cli -p 6379
127.0.0.1:6379> slaveof 20.20.50.26 6379
OK Already connected to specified master
- 进入20.20.50.28
执行相同命令, 加入集群:
[root@localhost local]# redis-cli -p 6379
127.0.0.1:6379> slaveof 20.20.50.26 6379
OK Already connected to specified master
- 进入20.20.50.26主节点, 验证查看集群信息:
[root@localhost redis-4.0.2]# redis-cli
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=20.20.50.28,port=6379,state=online,offset=2617,lag=0
slave1:ip=20.20.50.27,port=6379,state=online,offset=2617,lag=0
master_replid:c41563977b922b3877b4c470743efd1feb26099a
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:2617
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:2617
可以看到, connected_slaves显示连接的从节点数量为2, 三台节点成功组建主从集群。
- 配置自动创建
通过配置方式, 自动创建, 不需每次重启后, 都执行加入命令。
vi /etc/redis/6379.conf
增加配置:
slaveof 20.20.50.26 6379
- 验证
在从节点是无法写入数据:
[root@localhost redis-4.0.2]# redis-cli -h 20.20.50.28 -p 6379
20.20.50.28:6379> set test 123
(error) READONLY You can't write against a read only slave.
在20.20.50.26主节点, 可以写入数据:
127.0.0.1:6379> set test 123
OK
从节点查询是否能同步数据:
[root@localhost redis-4.0.2]# redis-cli -h 20.20.50.28 -p 6379
20.20.50.28:6379> get test
"123"
3.2.4 哨兵配置
在三台节点, 分别执行以下配置。
- 复制哨兵配置文件
cp /usr/local/redis-4.0.2/sentinel.conf /etc/redis/
- 修改配置文件
echo > /etc/redis/sentinel.conf
vi /etc/redis/sentinel.conf
增加以下内容:
# 绑定IP地址, 注意绑定IP时,要将外部通讯IP放置前面, 否则会出现找不到sentinel节点的情况
bind 20.20.50.26 127.0.0.1
# 绑定监听的端口
port 26379
# 是否开启保护模式, 如果绑定外部IP, 需要关闭访问保护
protected-mode no
# 后台方式运行
daemonize yes
# sentinel监控配置
sentinel monitor mymaster 20.20.50.26 6379 2
sentinel down-after-milliseconds mymaster 15000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 30000
配置说明:
sentinel monitor mymaster 20.20.50.26 6379 2: Sentinel去监视一个名为redis_master的主redis实例,IP地址为20.20.50.26,端口号为6379,将这个主实例判断为失效至少需要2个 Sentinel进程的同意。
sentinel down-after-milliseconds mymaster :指定了Sentinel认为Redis实例已经失效所需的毫秒数。当实例超过该时间没有返回PING,或者直接返回错误,那么Sentinel将这个实例标记为主观下线。
sentinel parallel-syncs mymaster 1:指定在执行故障转移时,最多有多少个从Redis实例在同步新的主实例,在从Redis实例较多的情况下这个数字越小,同步的时间越长,完成故障转移所需的时间就越长。
sentinel failover-timeout mymaster 30000:如果在该时间(ms)内未能完成failover操作,则认为该failover失败。
- 启动哨兵
在三台节点分别配置完成以后, 再执行启动命令:
redis-sentinel /etc/redis/sentinel.conf
如果出现警告提示:
WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
修改:
vi /etc/sysctl.conf
net.core.somaxconn=32768
sysctl -p
- 查看哨兵集群状态
[root@localhost ~]# redis-cli -p 26379
127.0.0.1:26379> 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=20.20.50.26:6379,slaves=2,sentinels=3
可以看到sentinel_masters数为1, sentinels总数为3,哨兵集群搭建成功。
3.2.5 哨兵功能验证
- 先进入哨兵集群, 查看当前状态
[root@localhost ~]# redis-cli -p 26379
127.0.0.1:26379> 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=20.20.50.26:6379,slaves=2,sentinels=3
- 停止Redis主节点服务, 再此查看状态信息
[root@localhost ~]# /etc/init.d/redis_6379 stop
Stopping ...
Waiting for Redis to shutdown ...
Redis stopped
查看哨兵集群信息(间隔数秒)
127.0.0.1:26379> 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=20.20.50.27:6379,slaves=2,sentinels=3
可以看到, Redis主节点已经从20.20.50.26转移至20.20.50.27服务器上。
- 验证单个哨兵节点的有效性
我们在哨兵配置中, sentinel monitor mymaster 20.20.50.26 6379 2 , 将哨兵投票数量设为2, 如果只存在1个哨兵节点, 是无法重新进行选主, 我们做个验证。
- 恢复Redis主从三个节点, 正常运行
[root@localhost ~]# /etc/init.d/redis_6379 start
Starting Redis server...
- 查看当前哨兵状态:
127.0.0.1:26379> 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=20.20.50.27:6379,slaves=2,sentinels=3
可以看到, 当前Redis主节点为20.20.50.27。
- 停止两个哨兵节点
停止20.20.50.27上的哨兵服务:
redis-cli -p 26379 shutdown
停止20.20.50.28上的哨兵服务:
redis-cli -p 26379 shutdown
- 停止20.20.50.27节点上的Redis服务
[root@localhost ~]# /etc/init.d/redis_6379 stop
Stopping ...
Waiting for Redis to shutdown ...
Redis stopped
- 再次查看哨兵状态
127.0.0.1:26379> 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=sdown,address=20.20.50.27:6379,slaves=2,sentinels=1
状态变为sdown, 但没有选举新的Redis主节点, 整个哨兵集群数量不够, 没有生效。
3.3 Redis集群模式部署
3.3.1 部署规划
Redis主从部署:
20.20.50.26: Redis主节点, 端口7000; Redis从节点, 端口7001。
20.20.50.27: Redis从节点, 端口7000; Redis从节点, 端口7001。
20.20.50.28: Redis从节点, 端口7000; Redis从节点, 端口7001。
3.3.2 Redis安装
参考上面3.2.2章节Redis安装, 确保Redis已成功安装。
3.3.3 Ruby安装
Redis集群创建工具需要依赖Ruby, 如果已安装, 并且版本在2.4.5以上, 可以忽略此步骤。
注意重新建立会话, 要使用ruby, 需要通过命令开启: scl enable rh-ruby24 bash
- 安装YUM源
yum install -y centos-release-scl-rh
- 安装ruby2.4以上版本
yum -y install rh-ruby24
- scl开启
scl enable rh-ruby24 bash
如果重新建立会话, 要再次开启。
- 查看ruby版本
[root@localhost src]# ruby -v
ruby 2.4.6p354 (2019-04-01 revision 67394) [x86_64-linux]
[root@localhost src]# gem -v
2.6.14.4
- gen安装redis插件
[root@localhost src]# gem install redis
Fetching: redis-4.1.3.gem (100%)
Successfully installed redis-4.1.3
Parsing documentation for redis-4.1.3
Installing ri documentation for redis-4.1.3
Done installing documentation for redis after 1 seconds
1 gem installed
3.3.4 集群安装配置
- 分别在三台节点上创建集群目录
mkdir -p /usr/local/redis_cluster/7000/
mkdir -p /usr/local/redis_cluster/7001/
- 集群配置文件
在每台节点上创建主从两个节点的配置文件:
vi /etc/redis/cluster-7000.conf
7000端口节点的配置内容:
# 绑定IP, 注意改成对应不同节点的IP地址
bind 20.20.50.26
port 7000
# 客户端连接密码
requirepass Redis1234{}
# 集群连接密码
masterauth Redis1234{}
# 是否后台运行
daemonize yes
pidfile /usr/local/redis_cluster/7000/redis.pid
appendonly yes
appendfilename "appendonly_7000.aof"
dir /usr/local/redis_cluster/7000/
dbfilename "dump_7000.rdb"
protected-mode no
cluster-enabled yes
cluster-config-file /usr/local/redis_cluster/7000/nodes.conf
# 集群超时时间
cluster-node-timeout 5000
# 表示负责一个插槽的主库下线且没有相应的从库进行故障恢复时,集群仍然可用
cluster-require-full-coverage no
maxmemory 100mb
# 缓存更新策略, 优先删除掉最近最不经常使用的key
maxmemory-policy allkeys-lru
maxclients 10000
继续增加7001的配置:
vi /etc/redis/cluster-7001.conf
配置内容:
bind 20.20.50.26
port 7001
# 客户端连接密码
requirepass Redis1234{}
# 集群连接密码
masterauth Redis1234{}
# 是否后台运行
daemonize yes
pidfile /usr/local/redis_cluster/7001/redis.pid
appendonly yes
appendfilename "appendonly_7001.aof"
dir /usr/local/redis_cluster/7001/
dbfilename "dump_7001.rdb"
protected-mode no
cluster-enabled yes
cluster-config-file /usr/local/redis_cluster/7001/nodes.conf
# 集群超时时间
cluster-node-timeout 5000
# 表示负责一个插槽的主库下线且没有相应的从库进行故障恢复时,集群仍然可用
cluster-require-full-coverage no
maxmemory 100mb
# 缓存更新策略, 优先删除掉最近最不经常使用的key
maxmemory-policy allkeys-lru
maxclients 10000
- 启动各集群节点
redis-server /etc/redis/cluster-7000.conf
redis-server /etc/redis/cluster-7001.conf
查看进程:
[root@localhost ~]# ps -ef | grep cluster
root 32933 1 0 00:01 ? 00:00:05 redis-server 20.20.50.26:7000 [cluster]
root 33038 1 0 00:03 ? 00:00:04 redis-server 20.20.50.26:7001 [cluster]
3.3.5 集群创建
- 执行命令
cd /usr/local/redis-4.0.2/src
./redis-trib.rb create --replicas 1 20.20.50.26:7000 20.20.50.26:7001 20.20.50.27:7000 20.20.50.27:7001 20.20.50.28:7000 20.20.50.28:7001
如果出现不能连接创建的错误:
[ERR] Sorry, can't connect to node 20.20.50.26:7000
先确保ruby的redis插件已安装, 检查所有节点是否都已正常启动。
gem install redis
如果还是出现错误, 需要修改ruby的redis配置文件, 加上认证密码:
vi /opt/rh/rh-ruby24/root/usr/local/share/gems/gems/redis-4.1.3/lib/redis/client.rb
修改:
class Redis
class Client
DEFAULTS = {
:url => lambda { ENV["REDIS_URL"] },
:scheme => "redis",
:host => "127.0.0.1",
:port => 6379,
:path => nil,
:timeout => 5.0,
:password => "Redis1234{}",
确认password输入正确。
- 再次执行集群创建命令
[root@localhost src]# ./redis-trib.rb create --replicas 1 20.20.50.26:7000 20.20.50.26:7001 20.20.50.27:7000 20.20.50.27:7001 20.20.50.28:7000 20.20.50.28:7001
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
20.20.50.26:7000
20.20.50.27:7000
20.20.50.28:7000
Adding replica 20.20.50.27:7001 to 20.20.50.26:7000
Adding replica 20.20.50.26:7001 to 20.20.50.27:7000
Adding replica 20.20.50.28:7001 to 20.20.50.28:7000
M: 770036cb37f3fd9623e79b677e032488d307929e 20.20.50.26:7000
slots:0-5460 (5461 slots) master
S: b88106a9c1da2ccf706f0348527c5c411ce53276 20.20.50.26:7001
replicates f75cf9009d46f462b34c917fec35c9307a0f3778
M: f75cf9009d46f462b34c917fec35c9307a0f3778 20.20.50.27:7000
slots:5461-10922 (5462 slots) master
S: 82ff87e5f73422a213951467b70fbc409bc68e51 20.20.50.27:7001
replicates 770036cb37f3fd9623e79b677e032488d307929e
M: b3687887df3db9333c455044866dbaa21b786fc9 20.20.50.28:7000
slots:10923-16383 (5461 slots) master
S: 0cfa4bdcd26c5a974ef64b606452bcf70de5d7b9 20.20.50.28:7001
replicates b3687887df3db9333c455044866dbaa21b786fc9
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 20.20.50.26:7000)
M: 770036cb37f3fd9623e79b677e032488d307929e 20.20.50.26:7000
slots:0-5460 (5461 slots) master
1 additional replica(s)
M: f75cf9009d46f462b34c917fec35c9307a0f3778 20.20.50.27:7000
slots:5461-10922 (5462 slots) master
1 additional replica(s)
S: b88106a9c1da2ccf706f0348527c5c411ce53276 20.20.50.26:7001
slots: (0 slots) slave
replicates f75cf9009d46f462b34c917fec35c9307a0f3778
S: 0cfa4bdcd26c5a974ef64b606452bcf70de5d7b9 20.20.50.28:7001
slots: (0 slots) slave
replicates b3687887df3db9333c455044866dbaa21b786fc9
S: 82ff87e5f73422a213951467b70fbc409bc68e51 20.20.50.27:7001
slots: (0 slots) slave
replicates 770036cb37f3fd9623e79b677e032488d307929e
M: b3687887df3db9333c455044866dbaa21b786fc9 20.20.50.28:7000
slots:10923-16383 (5461 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.
按提示选择yes继续处理, 可以在最后面看到所有slots分配成功的提示。
- 集群的信息查看
连接到集群节点:
redis-cli -h 20.20.50.26 -p 7000
查看集群状态信息:
20.20.50.26:7000> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:13
cluster_my_epoch:10
cluster_stats_messages_ping_sent:6837
cluster_stats_messages_pong_sent:5196
cluster_stats_messages_fail_sent:64
cluster_stats_messages_auth-req_sent:5
cluster_stats_messages_auth-ack_sent:3
cluster_stats_messages_sent:12105
cluster_stats_messages_ping_received:5193
cluster_stats_messages_pong_received:5529
cluster_stats_messages_fail_received:30
cluster_stats_messages_auth-req_received:4
cluster_stats_messages_auth-ack_received:2
cluster_stats_messages_update_received:2
cluster_stats_messages_received:10760
3.4 应用项目连接配置
Spring Data Redis 在1.7版本开始支持Redis的集群,如果非1.7版本,需要自己实现Redis集群的支持。这里采用Spring Data Redis版本为2.1.9.RELEASE, 可以直接支持集群使用。
- 修改项目配置
修改配置文件bootstrap.yml:
spring:
redis:
# host: 127.0.0.1
# password:
# port: 6379
# jedis 连接池配置
jedis:
pool:
max-wait: 6000
min-idle: 2
max-idle: 8
# 集群节点配置
cluster:
nodes: 20.20.50.26:7000,20.20.50.26:7001,20.20.50.27:7000,20.20.50.27:7001,20.20.50.28:7000,20.20.50.28:7001
# 集群连接超时配置
timeout: 2000
# 密码认证配置
password: 654321
将原来的单节点连接配置注释, 增加集群与连接池配置。 只修改配置文件即可, 其他不用修改。
连接缓存集群节点, 查看缓存信息
[root@localhost ~]# redis-cli -c -h 20.20.50.26 -p 7000
20.20.50.26:7000> auth Redis1234{}
OK
20.20.50.26:7000> keys *
1) "account:warn:setting:stockId:1::1"
-c 开启集群模式, 能够支持重定向操作。
4. 合理
- 这里讲解了Redis所支持的部署模式, 并实际搭建部署了哨兵模式与多主集群模式,哨兵模式实际上是包含了主从模式的搭建,基本上是覆盖了Redis的所有部署模式。
- 每种部署模式都有其适合的场景, 因地制宜, 合理选择, 更为简单高效的使用Redis, 当然java端的集群连接组件有很多, Spring Boot 从2.0开始, Redis的连接组件从Jedis替换为Lettuce, 不同客户端有不同的特性与实现机制,具体大家可以再去搜寻相关资料, 这里就不再赘述。