redis高可用与集群
虽然Redis可以实现单机的数据持久化,但无论是RDB也好或者AOF也好,都解决不了单点宕机问题,即一旦redis服务器本身出现系统故障、硬件故障等问题后,就会直接造成数据的丢失因此需要使用另外的技术来解决单点问题。
配置 reids 主从:
主备模式,可以实现Redis数据的跨主机备份。程序端连接到高可用负载的VIP,然后连接到负载服务器设置的Redis后端realserver,此模式不需要在程序里面配置Redis服务器的真实IP地址,当后期Redis服务器IP地址发生变更,只需要更改redis相应的后端realserver即可,可避免更改程序中的IP地址设置。
Slave主要配置:
Redis Slave也要开启持久化,并设置和master同样的连接密码,因为后期slave会有提升为master的可能Slave端切换master同步后会丢失之前的所有数据。
一旦某个Slave成为一个master的slave,Redis Slave服务会清空当前redis服务器上的所有数据,并将master的数据导入到自己的内存,但是断开同步关系后,不会删除当前已经同步过的数据。
命令行配置:
当前状态为master,需要转换为slave角色,并指向master服务器的IP+PORT+Password
172.16.36.51:6379> SLAVEOF 172.16.36.50 6379
OK
172.16.36.51:6379> CONFIG SET masterauth redis
OK
同步日志:

保存配置到redis.conf
286 replicaof 172.16.36.50 6379
293 masterauth redis #master 如果 密码需要设置
重启slave验证:
重启之后进入redis-cli,info Replication,master_link_status必须为UP状态,salve状态下只读数据,无法进行写入。

slave切换master,停止slave同步,并查看当前状态::
172.16.36.51:6379> info Replication
# Replication
role:slave
master_host:172.16.36.50
master_port:6379
master_link_status:up
master_last_io_secon ds_ago:8
master_sync_in_progress:0
172.16.36.51:6379> SLAVEOF no one #停止slave,自动切换为主节点
OK
172.16.36.51:6379> info Replication
# Replication
role:master
connected_slaves:0
Slave节点再有Slave

在有slave的master查看状态:
# Replication
role:slave
master_host:172.16.36.51
master_port:6379
master_link_status:up
master_last_io_seconds_ago:9 # 最近 一次与 master 通信已经过去多少秒 。
master_sync_in_progress:0 # 是否正在与 master 通信 。
slave_ repl_offset:5334 # 当前 同步的偏移 量。
slave_priority:100 #slave 优先级, master 故障后值越小 越优先同步 。
slave_read_only:1
connected_slaves:1
slave0:ip=192.168.7.104,port=6379,state=online,offset=5334,lag=1
master_replid:0f0318c25a022add7fd51d4438c470cf608631f9
master_replid2:00000000 00000000000000000000000000000000
master_repl_offset:5334
second_repl_offset: 1
repl_backlog_active:1
Redis Master
Redis Slave
Redis Slave
Redis Slave
repl_backlog_size:1048576repl_backlog_size:1048576 repl_backlog_first_byte_offset:1repl_backlog_first_byte_offset:1 repl_backlog_histlen:5334repl_backlog_histlen:5334
主从复制过程:
Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:
1. )从服务器连接主服务器,发送SYNC命令;
2. )主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB快照文件并使用缓冲区记录此后执行的所有写命令;
3. )主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
4. )从服务器收到快照文件后丢弃所有旧数据,载入收到的快照
5. )主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令
6. )从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令
7. )后期同步会先发送自己slave_repl_offset位置只同步新增加的数据不再全量同步。

常见问题汇总
-
- master密码不对:即配置的master密码不对,导致验证不通过,而无法建立主从同步关系。
-
- Redis版本不一致:master和slave之间必须保持版本一致。
-
- 无法远程连接:在开启了安全模式情况下,没有设置bind地址和密码。
redis集群
-
- 以上redis主从均需要手动切换,无法做到高可用,即当master宕机,slave自动提升为master。
-
- 当单台Redis服务器性能无法满足业务写入需求的时候,就必须需要一种方式解决以上的两个核心问题,即:
- 1.master和slave角色的无缝切换,让业务无感知从而不影响业务使用
- 2可以横向动态扩展Redis服务器,从而实现多台服务器并行写入以实现更高并发的目的。
-
- Redis集群实现方式: 客户端分片 代理分片 Redis Cluster
Sentinel(哨兵)
-
- sentinel进程是用于监控redis集群中Master主服务器工作的状态,
-
- 在Master主服务器发生故障的时候,可以实现Master和Slave服务器的切换,保证系统的高可用,
-
- 其已经被集成在redis2.6+的版本中,Redis的哨兵模式到了2.8版本之后就稳定了下来。
-
- 一般在生产环境也建议使用Redis的2.8版本的以后版本。
-
- 哨兵(Sentinel)是一个分布式系统,
-
- 可以在一个架构中运行多个哨兵(sentinel)进程,
-
- 这些进程使用流言协议(gossipprotocols)来接收关于Master主服务器是否下线的信息,
-
- 并使用投票协议(Agreement来决定是否执行自动故障迁移以及选择哪个Slave作为新的Master。
-
- 每个哨兵进程会向其它哨兵(Master、Slave定时发送消息,以确认对方是否”活”着,如果发现对方在指定配置时间可配置的内未得到回应,则暂时认为对方已掉线,也就是所谓的”主观认为宕机”
-
- 主观是每个成员都具有的独自的而且可能相同也可能不同的意识,英文名称Subjective Down,简称SDOWN。
-
- 有主观宕机,肯定就有客观宕机。
-
- 当“哨兵群”中的多数Sentinel进程在对Master主服务器做出SDOWN的判断,并且通过SENTINEL is master down by addr命令互相交流之后,得出的MasterServer下线判断,这种方式就是“客观宕机”,
-
- 客观是不依赖于某种意识而已经实际存在的一切事物英文名称是:Objectively Down,简称ODOWN。
-
- 通过一定的vote算法,从剩下的slave从服务器节点中,选一台提升为Master服务器节点,然后自动修改相关配置,并开启故障转移(failover)。
-
- Sentinel机制可以解决master和slave角色的切换问题。
手动配置master环境:
需要手动先指定某一台Redis服务器为master,然后将其他slave服务器使用命令配置为master服务器的slave哨兵的前提是已经手动实现了一个redis master slave的运行环境。

master-redis为主redis,slave1-redis1和slave2-redis2为从redis。

#分别在三台机器执行shell命令,启动redis,并随开机自启动:
# systemctl start redis && systemctl enable redis
[root@master-redis ~]# ss -tnl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 80 *:3306 *:*
LISTEN 0 511 172.16.36.50:6379 *:*
LISTEN 0 511 127.0.0.1:6379 *:*
#连接redis并查看同步信息,显示有2个slave在线。
[root@master-redis ~]# redis-cli -h 172.16.36.50
172.16.36.50:6379> AUTH redis
OK
172.16.36.50:6379> INFO replication
# Replication
role:master
connected_slaves:2
slave0:ip=172.16.36.51,port=6379,state=online,offset=1190,lag=0
slave1:ip=172.16.36.52,port=6379,state=online,offset=1190,lag=1
master_replid:56bc9d131af0c583b303d336221a6369d0ac46c1
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1190
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1190
=========================================================
[root@slave1-redis1 ~]# ss -tnl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 80 *:3306 *:*
LISTEN 0 128 172.16.36.51:6379 *:*
LISTEN 0 128 127.0.0.1:6379 *:*
#从redis1:配置文件内配置主服务器IP,端口和密码
[root@slave1-redis1 ~]# vim /apps/redis/etc/redis.conf
282 slaveof 172.16.36.50 6379
290 masterauth redis
#连接redis并查看同步信息
[root@slave1-redis1 ~]# redis-cli -h 172.16.36.51
172.16.36.51:6379> AUTH redis
OK
172.16.36.51:6379> INFO replication
# Replication
role:slave
master_host:172.16.36.50
master_port:6379
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_repl_offset:1148
slave_priority:100
slave_read_only:1
connected_slaves:0
=========================================================
[root@slave2-redis2 ~]# ss -tnl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 80 *:3306 *:*
LISTEN 0 128 172.16.36.52:6379 *:*
#从redis2:配置文件内配置主服务器IP,端口和密码
[root@slave2-redis2 ~]# vim /apps/redis/etc/redis.conf
282 slaveof 172.16.36.50 6379
290 masterauth redis
#连接redis并查看同步信息
[root@slave2-redis2 ~]# redis-cli -h 172.16.36.52
172.16.36.52:6379> AUTH redis
OK
172.16.36.52:6379> INFO replication
# Replication
role:slave
master_host:172.16.36.50
master_port:6379
master_link_status:up
master_last_io_seconds_ago:5
master_sync_in_progress:0
slave_repl_offset:1050
slave_priority:100
slave_read_only:1
应用程序如何连接redis?
- java客户端连接redis是通过Jedis来实现的,
- java代码用的时候只要创建Jedis对象就可以建多个Jedis连接池来连接redis,应用程序再直接调用连接池即可连接Redis。
- 而Redis为了保障高可用,服务一般都是Sentinel部署方式,
- 当Redis服务中的主服务挂掉之后,会仲裁出另外一台Slaves服务充当Master。
- 这个时候,我们的应用即使使用了Jedis连接池,Master服务挂了,我们的应用将还是无法连接新的Master服务,
- 为了解决这个问题,Jedis也提供了相应的Sentinel实现,能够在RedisSentinel主从切换时候,通知我们的应用,把我们的应用连接到新的Master服务。
- Redis Sentinel的使用也是十分简单的,
- 只是在JedisPool中添加了Sentinel和MasterName参数,JRedisSentinel底层基于Redis订阅实现Redis主从服务的切换通知,
- 当Reids发生主从切换时,Sentinel会发送通知主动通知Jedis进行连接的切,
- Jedis Sentinel Pool在每次从连接池中获取链接对象的时候,都要对连接对象进行检测,
- 如果此链接和Sentinel的Master服务连接参数不一致,则会关闭此连接,重新获取新的Jedis连接对象。
Redis官方客户端:https://redis.io/clients
python连接redis
# yum install python-pip
# pip install redis
[root@redis s3 ~]# cat test.py
#!/bin/env python
#Author: ZhangJie
import redis
pool = redis.ConnectionPool(host="192.168.7.101", port=6379,password="123456")
r = redis.Redis(connecti on_pool=pool)
for i in range(100):
r.set("k%d" % i,"v%d" % i)
data=r.get("k%d" % i)
print(data)
编辑配置文件sentinel.conf
哨兵可以不和Redis服务器部署在一起,但配置在一起的话,节省资源。
源码编译的redis,哨兵的配置文件在源码包里,需要自行拷贝到配置文件存储目录中。
[root@master-redis redis-4.0.14]# cp sentinel.conf /apps/redis/etc/sentinel.conf
[root@master-redis redis-4.0.14]# chown -R redis.redis /apps/redis
master-redis哨兵配置:
[root@master-redis redis-4.0.14]# grep "^[a-Z]" /apps/redis/etc/sentinel.conf
bind 172.16.36.50
port 26379
dir "/apps/redis/log"
logfile "sentinel_26379.log"
pidfile "redis_sentinel.pid"
#法定人数限制(quorum),即有几个slave认为masterdown了就进行故障转移
sentinel monitor mymaster 172.16.36.50 6379 2
sentinel auth-pass mymaster redis
#SDOWN主观下线的时间,毫秒,即30s。
sentinel down-after-milliseconds mymaster 30000
#发生故障转移时候同时向新master同步数据的slave数量,数字越小总同步时间越长,顺序同步
sentinel parallel-syncs mymaster 1
#所有slaves指向新的master所需的超时时间
sentinel failover-timeout mymaster 180000
#禁止修改脚本:内部可自定义监控脚本。
sentinel deny-scripts-reconfig yes
slave1-redis1哨兵配置:
[root@slave1-redis1 ~]# grep "^[a-Z]" /apps/redis/etc/sentinel.conf
bind 172.16.36.51
port 26379
dir "/apps/redis/log"
logfile "sentinel_26379.log"
pidfile "redis_sentinel.pid"
sentinel monitor mymaster 172.16.36.50 6379 2
sentinel auth-pass mymaster redis
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
slave2-redis2哨兵配置:
[root@slave2-redis2 ~]# grep "^[a-Z]" /apps/redis/etc/sentinel.conf
bind 172.16.36.52
port 26379
dir "/apps/redis/log"
logfile "sentinel_26379.log"
pidfile "redis_sentinel.pid"
sentinel monitor mymaster 172.16.36.50 6379 2
sentinel auth-pass mymaster redis
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
启动哨兵:
三台哨兵都要启动
[root@master-redis ~]# redis-sentinel /apps/redis/etc/sentinel.conf
[root@slave1-redis1 ~]# redis-sentinel /apps/redis/etc/sentinel.conf
[root@slave2-redis2 ~]# redis-sentinel /apps/redis/etc/sentinel.conf
端口验证:
[root@master-redis ~]# ss -tnl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 80 *:3306 *:*
LISTEN 0 511 172.16.36.50:26379 *:*
LISTEN 0 511 172.16.36.50:6379 *:*
哨兵日志:
当手动停止主redis服务后,等待30s可以从哨兵日志中看到一些主从切换过程:
[root@slave1-redis1 ~]# tail -f /apps/redis/log/sentinel_26379.log

故障转移后的主redis配置文件:
宕机的主服务器需手动指定slaveof+IP+Port和新redis主服务器密码,重新启动服务上线
[root@master-redis ~]# vim /apps/redis/etc/redis.conf
282 slaveof 172.16.36.51 6379
290 masterauth redis
故障转移后的主sentinel配置文件:
sentinel配置文件中master指向会被哨兵自动修改为新redis主服务器,重启redis服务后即可上线
[root@master-redis ~]# vim /apps/redis/etc/sentinel.conf
106 sentinel monitor mymaster 172.16.36.51 6379 2
114 sentinel auth-pass mymaster redis
切换后的主服务器:
[root@slave1-redis1 ~]# redis-cli -h 172.16.36.51 -p 26379
172.16.36.51: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=172.16.36.51:6379,slaves=2,sentinels=3
[root@slave1-redis1 ~]# redis-cli -h 172.16.36.51
172.16.36.51:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=172.16.36.52,port=6379,state=online,offset=593188,lag=0
slave1:ip=172.16.36.50,port=6379,state=online,offset=593188,lag=0
master_replid:72458436e940c5bd46a89da0cab5f44f01656741
master_replid2:c5ec6e007bd7b631106ff2d2b774b839d9ae7ebd
master_repl_offset:593188
second_repl_offset:333965
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:593188
切换后的redis2-slave2服务器:
[root@slave2-redis2 ~]# redis-cli -h 172.16.36.52
172.16.36.52:6379> info replication
# Replication
role:slave
master_host:172.16.36.51
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:749640
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:72458436e940c5bd46a89da0cab5f44f01656741 #现任master
master_replid2:c5ec6e007bd7b631106ff2d2b774b839d9ae7ebd #前任master
master_repl_offset:749640
second_repl_offset:333965
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:749640
Redis Cluster
Redis分布式部署方案
- 1)客户端分区:由客户端程序决定key写分配和写入的redis node但是需要客户端自己处理写入分配、高可用管理和故障转移等
- 2)代理方案:基于三方软件实现redis proxy,客户端先连接之代理层,由代理层实现key的写入分配,对客户端来说是有比较简单,但是对于集群管节点增减相对比较麻烦,而且代理本身也是单点和性能瓶颈。
-
- 在哨兵sentinel机制中,可以解决redis高可用的问题,
-
- 即当master故障后,可以自动将slave提升为master从而可以保证redis服务的正常使用,
-
- 但是无法解决redis单机写入的瓶颈问题即单机的redis写入性能受限于单机的内存大小、并发数量、网卡速率等因素,
-
- 因此redis官方在redis3.0版本之后推出了无中心架构的redis cluster机制,
-
- 在无中心的redis集群汇中,其每个节点保存当前节点数据和整个集群状态每个节点都和其他所有节点连接
-
- 特点如下:
- 1:所有Redis节点使用PING机制互联
- 2:集群中某个节点的失效是整个集群中超过半数的节点监测都失效才算真正的失效
- 3:客户端不需要proxy,即可直接连接redis应用程序需要写全部的redis服务器IP。
- 4:redis cluster把所有的redis node映射到0-16383个槽位slot上,读写需要到指定的redis node上进行操作,因此有多少个reids node,相当于redis并发扩展了多少倍。
- 5:Redis cluster预先分配16384个slot槽位,当需要在redis集群中写入一个keyvalue的时候,会使用CRC16(mod16384之后的值,决定将key写入值哪一个槽位从而决定写入哪一个Redis节点上,从而有效解决单机瓶颈。
Redis cluster架构
Redis cluster基本架构:
假如三个主节点分别是:A,B,C三个节点,采用哈希槽(hash的方式来分配16384个slot的话,
它们三个节点分别承担的slot区间是:
节点A覆盖0-5460
节点B覆盖5461-10922
节点C覆盖10923-16383

Redis cluster主从架构:
Redis cluster的架构虽然解决了并发的问题,但是又引入了一个新的问题,每个Redis master的高可用如何解决。

部署redis集群
环境准备:
– 环境A 三台服务器,每台服务器启动6379和6380,两个redis服务。
172.16.36.50:6379 /6380 172.16.36.51 6379 /6380
172.16.36.52:6379 /6380
另外预留一台服务器做集群 添加节点测试。
172.16.36.53:6379 /6380
- 单台服务器按端口启动redis
[root@master-redis ~]# redis-server /apps/redis/etc/redis.conf --port 6379
[root@master-redis ~]# redis-server /apps/redis/etc/redis.conf --port 6380
[root@master-redis ~]# ss -tnl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 80 *:3306 *:*
LISTEN 0 511 172.16.36.50:6379 *:*
LISTEN 0 511 172.16.36.50:6380 *:*
- 环境 B 生产环境建议直接 6 台 服务器。
创建redis cluster集群的前提:
- 每个redis node节点采用相同的硬件配置、相同的密码
- 每个节点必须开启的参数
clusterenabled yes#必须开启集群状态,开启后redis进程会有cluster显示
814 cluster-enabled yes
cluster-config-file-nodes-6380.conf#此文件有redis cluster集群自动创建和维护,不需要任何手动操作
822 cluster-config-file nodes-6379.conf - 所有redis服务器必须没有任何数据
- 先启动为单机redis且没有任何keyvalue
创建集群:
Redis 3和4版本
-
- 需要使用到集群管理工具redistrib.rb
-
- 这个工具是redis官方推出的管理redis集群的工具,集成在redis的源码src目录下,
-
- 是基于redis提供的集群命令封装成简单、便捷、实用的操作工具,
-
- redistrib.rb是redis作者用ruby开发完成的,
-
- centos系统yum安装的ruby存在版本较低问题,如下:
[root@s1 ~]# yum install ruby rubygems y
[root@s1 ~]# find / -name redis-trib.rb /usr/local/src/redis4.0.14/src/redis trib.rb
[root@s1 ~]# cp /usr/local/src/redis4.0.14/src/redis-trib.rb /usr/bin/
[root@s1 src]# gem install redis
Fet
ching: redis-4.1.2.gem (100%)
ERROR: Error installing redis:
redis requires Ruby version >= 2.3.0.
解决 ruby 版本较低问题:
[root@s1 src]# wget https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.5.tar.gz
[root@s1 src]# tar xf ruby-2.5.5.tar.gz
[root@s1 src]# cd ruby2.5.5
[root@s1 ruby-2.5.5 ]# ./configure
[root@s1 ruby-2.5.5 ]# make j 2
[root@s1 ruby-2.5.5 ]# make install
[root@s1 ruby-2.5.5 ]# gem install redis #https://rubygems.org/gems/redis # gem install -l redis 3.3.0.gem
验证redis-trib.rb命令是否可执行:
[root@master-redis ruby-2.5.5]# redis-trib.rb
Usage: redis-trib <command> <options> <arguments ...>
create host1:port1 ... hostN:portN # 创建集群
--replicas <arg> # 指定master的副本数量
check host:port #检查集群主机信息
info host:port #查看集群主机信息
fix host:port #修复集群
--timeout <arg>
reshard host:port #在线热迁移集群指定主机的slots数据
--from <arg>
--to <arg>
--slots <arg>
--yes
--timeout <arg>
--pipeline <arg>
rebalance host:port #平衡集群中各主机的slot数量
--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 #导入外部redis服务器的数据到当前节点
--from <arg>
--copy
--replace
help (show this help)
修改密码为redis登录密码
[root@master-redis ~]# vim /usr/local/lib/ruby/gems/2.5.0/gems/redis-4.1.2/lib/redis/client.rb

创建redis cluster集群:
Redis 3 /4版本:
[root@master-redis ~]# redis-trib.rb create --replicas 1 \
172.16.36.50:6379 172.16.36.51:6379 172.16.36.52:6379 \
172.16.36.50:6380 172.16.36.51:6380 172.16.36.52:6380
创建集群时,常见的几种报错:
-
- 某台节点的redis服务没启动:

- 某台节点的redis服务没启动:
-
- 某台节点的redis数据目录不为空:

- 某台节点的redis数据目录不为空:
Redis 5版本
[root@master-redis ~]# redis-cli -a redis --cluster create \
172.16.36.50:6379 172.16.36.51:6379 172.16.36.52:6379 \
172.16.36.50:6380 172.16.36.51:6380 172.16.36.52:6380 \
--cluster-replicas 1
集群对应关系(1<–5;2<–6;3<–4):

后期通过脚本查看cluster info信息,取出”cluster_state:ok”中的运行状况“ok”,即可确定集群运行状态:
