一文掌握Redis主从复制、哨兵、Cluster三种集群模式

在开发测试环境中,咱们通常搭建Redis的单实例来应对开发测试需求,可是在生产环境,若是对可用性、可靠性要求较高,则须要引入Redis的集群方案。虽然如今各大云平台有提供缓存服务能够直接使用,但了解一下其背后的实现与原理总仍是有些必要(好比面试), 本文就一块儿来学习一下Redis的几种集群方案。html

Redis支持三种集群方案node

  • 主从复制模式
  • Sentinel(哨兵)模式
  • Cluster模式

主从复制模式

1. 基本原理

主从复制模式中包含一个主数据库实例(master)与一个或多个从数据库实例(slave),以下图面试

redis-master-slave

客户端可对主数据库进行读写操做,对从数据库进行读操做,主数据库写入的数据会实时自动同步给从数据库。redis

具体工做机制为:算法

  1. slave启动后,向master发送SYNC命令,master接收到SYNC命令后经过bgsave保存快照(即上文所介绍的RDB持久化),并使用缓冲区记录保存快照这段时间内执行的写命令
  2. master将保存的快照文件发送给slave,并继续记录执行的写命令
  3. slave接收到快照文件后,加载快照文件,载入数据
  4. master快照发送完后开始向slave发送缓冲区的写命令,slave接收命令并执行,完成复制初始化
  5. 此后master每次执行一个写命令都会同步发送给slave,保持master与slave之间数据的一致性

2. 部署示例

本示例基于Redis 5.0.3版。shell

redis.conf的主要配置数据库

###网络相关###
# bind 127.0.0.1 # 绑定监听的网卡IP,注释掉或配置成0.0.0.0可以使任意IP都可访问
protected-mode no # 关闭保护模式,使用密码访问
port 6379  # 设置监听端口,建议生产环境均使用自定义端口
timeout 30 # 客户端链接空闲多久后断开链接,单位秒,0表示禁用

###通用配置###
daemonize yes # 在后台运行
pidfile /var/run/redis_6379.pid  # pid进程文件名
logfile /usr/local/redis/logs/redis.log # 日志文件的位置

###RDB持久化配置###
save 900 1 # 900s内至少一次写操做则执行bgsave进行RDB持久化
save 300 10
save 60 10000 
# 若是禁用RDB持久化,可在这里添加 save ""
rdbcompression yes #是否对RDB文件进行压缩,建议设置为no,以(磁盘)空间换(CPU)时间
dbfilename dump.rdb # RDB文件名称
dir /usr/local/redis/datas # RDB文件保存路径,AOF文件也保存在这里

###AOF配置###
appendonly yes # 默认值是no,表示不使用AOF增量持久化的方式,使用RDB全量持久化的方式
appendfsync everysec # 可选值 always, everysec,no,建议设置为everysec

###设置密码###
requirepass 123456 # 设置复杂一点的密码
复制代码

部署主从复制模式只需稍微调整slave的配置,在redis.conf中添加缓存

replicaof 127.0.0.1 6379 # master的ip,port
masterauth 123456 # master的密码
replica-serve-stale-data no # 若是slave没法与master同步,设置成slave不可读,方便监控脚本发现问题
复制代码

本示例在单台服务器上配置master端口6379,两个slave端口分别为7001,7002,启动master,再启动两个slavebash

[root@dev-server-1 master-slave]# redis-server master.conf
[root@dev-server-1 master-slave]# redis-server slave1.conf
[root@dev-server-1 master-slave]# redis-server slave2.conf
复制代码

进入master数据库,写入一个数据,再进入一个slave数据库,当即即可访问刚才写入master数据库的数据。以下所示服务器

[root@dev-server-1 master-slave]# redis-cli 
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> set site blog.jboost
OK
127.0.0.1:6379> get site
"blog.jboost"
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=7001,state=online,offset=13364738,lag=1
slave1:ip=127.0.0.1,port=7002,state=online,offset=13364738,lag=0
...
127.0.0.1:6379> exit

[root@dev-server-1 master-slave]# redis-cli -p 7001
127.0.0.1:7001> auth 123456
OK
127.0.0.1:7001> get site
"blog.jboost"
复制代码

执行info replication命令能够查看链接该数据库的其它库的信息,如上可看到有两个slave链接到master

3. 主从复制的优缺点

优势:

  1. master能自动将数据同步到slave,能够进行读写分离,分担master的读压力
  2. master、slave之间的同步是以非阻塞的方式进行的,同步期间,客户端仍然能够提交查询或更新请求

缺点:

  1. 不具有自动容错与恢复功能,master或slave的宕机均可能致使客户端请求失败,须要等待机器重启或手动切换客户端IP才能恢复
  2. master宕机,若是宕机前数据没有同步完,则切换IP后会存在数据不一致的问题
  3. 难以支持在线扩容,Redis的容量受限于单机配置

Sentinel(哨兵)模式

1. 基本原理

哨兵模式基于主从复制模式,只是引入了哨兵来监控与自动处理故障。如图

redis-sentinel

哨兵顾名思义,就是来为Redis集群站哨的,一旦发现问题能作出相应的应对处理。其功能包括

  1. 监控master、slave是否正常运行
  2. 当master出现故障时,能自动将一个slave转换为master(大哥挂了,选一个小弟上位)
  3. 多个哨兵能够监控同一个Redis,哨兵之间也会自动监控

哨兵模式的具体工做机制:

在配置文件中经过 sentinel monitor <master-name> <ip> <redis-port> <quorum> 来定位master的IP、端口,一个哨兵能够监控多个master数据库,只须要提供多个该配置项便可。哨兵启动后,会与要监控的master创建两条链接:

  1. 一条链接用来订阅master的_sentinel_:hello频道与获取其余监控该master的哨兵节点信息
  2. 另外一条链接按期向master发送INFO等命令获取master自己的信息

与master创建链接后,哨兵会执行三个操做:

  1. 按期(通常10s一次,当master被标记为主观下线时,改成1s一次)向master和slave发送INFO命令
  2. 按期向master和slave的_sentinel_:hello频道发送本身的信息
  3. 按期(1s一次)向master、slave和其余哨兵发送PING命令

发送INFO命令能够获取当前数据库的相关信息从而实现新节点的自动发现。因此说哨兵只须要配置master数据库信息就能够自动发现其slave信息。获取到slave信息后,哨兵也会与slave创建两条链接执行监控。经过INFO命令,哨兵能够获取主从数据库的最新信息,并进行相应的操做,好比角色变动等。

接下来哨兵向主从数据库的_sentinel_:hello频道发送信息与一样监控这些数据库的哨兵共享本身的信息,发送内容为哨兵的ip端口、运行id、配置版本、master名字、master的ip端口还有master的配置版本。这些信息有如下用处:

  1. 其余哨兵能够经过该信息判断发送者是不是新发现的哨兵,若是是的话会建立一个到该哨兵的链接用于发送PING命令。
  2. 其余哨兵经过该信息能够判断master的版本,若是该版本高于直接记录的版本,将会更新
  3. 当实现了自动发现slave和其余哨兵节点后,哨兵就能够经过按期发送PING命令定时监控这些数据库和节点有没有中止服务。

若是被PING的数据库或者节点超时(经过 sentinel down-after-milliseconds master-name milliseconds 配置)未回复,哨兵认为其主观下线(sdown,s就是Subjectively —— 主观地)。若是下线的是master,哨兵会向其它哨兵发送命令询问它们是否也认为该master主观下线,若是达到必定数目(即配置文件中的quorum)投票,哨兵会认为该master已经客观下线(odown,o就是Objectively —— 客观地),并选举领头的哨兵节点对主从系统发起故障恢复。若没有足够的sentinel进程赞成master下线,master的客观下线状态会被移除,若master从新向sentinel进程发送的PING命令返回有效回复,master的主观下线状态就会被移除

哨兵认为master客观下线后,故障恢复的操做须要由选举的领头哨兵来执行,选举采用Raft算法:

  1. 发现master下线的哨兵节点(咱们称他为A)向每一个哨兵发送命令,要求对方选本身为领头哨兵
  2. 若是目标哨兵节点没有选过其余人,则会赞成选举A为领头哨兵
  3. 若是有超过一半的哨兵赞成选举A为领头,则A当选
  4. 若是有多个哨兵节点同时参选领头,此时有可能存在一轮投票无竞选者胜出,此时每一个参选的节点等待一个随机时间后再次发起参选请求,进行下一轮投票竞选,直至选举出领头哨兵

选出领头哨兵后,领头者开始对系统进行故障恢复,从出现故障的master的从数据库中挑选一个来当选新的master,选择规则以下:

  1. 全部在线的slave中选择优先级最高的,优先级能够经过slave-priority配置
  2. 若是有多个最高优先级的slave,则选取复制偏移量最大(即复制越完整)的当选
  3. 若是以上条件都同样,选取id最小的slave

挑选出须要继任的slave后,领头哨兵向该数据库发送命令使其升格为master,而后再向其余slave发送命令接受新的master,最后更新数据。将已经中止的旧的master更新为新的master的从数据库,使其恢复服务后以slave的身份继续运行。

2. 部署演示

本示例基于Redis 5.0.3版。

哨兵模式基于前文的主从复制模式。哨兵的配置文件为sentinel.conf,在文件中添加

sentinel monitor mymaster 127.0.0.1 6379 1 # mymaster定义一个master数据库的名称,后面是master的ip, port,1表示至少须要一个Sentinel进程赞成才能将master判断为失效,若是不知足这个条件,则自动故障转移(failover)不会执行
sentinel auth-pass mymaster 123456 # master的密码

sentinel down-after-milliseconds mymaster 5000 # 5s未回复PING,则认为master主观下线,默认为30s
sentinel parallel-syncs mymaster 2  # 指定在执行故障转移时,最多能够有多少个slave实例在同步新的master实例,在slave实例较多的状况下这个数字越小,同步的时间越长,完成故障转移所需的时间就越长
sentinel failover-timeout mymaster 300000 # 若是在该时间(ms)内未能完成故障转移操做,则认为故障转移失败,生产环境须要根据数据量设置该值
复制代码

一个哨兵能够监控多个master数据库,只需按上述配置添加多套

分别以26379,36379,46379端口启动三个sentinel

[root@dev-server-1 sentinel]# redis-server sentinel1.conf --sentinel
[root@dev-server-1 sentinel]# redis-server sentinel2.conf --sentinel
[root@dev-server-1 sentinel]# redis-server sentinel3.conf --sentinel
复制代码

也可使用redis-sentinel sentinel1.conf 命令启动。此时集群包含一个master、两个slave、三个sentinel,如图,

redis-cluster-instance

咱们来模拟master挂掉的场景,执行 kill -9 3017 将master进程干掉,进入slave中执行 info replication查看,

[root@dev-server-1 sentinel]# redis-cli -p 7001
127.0.0.1:7001> auth 123456
OK
127.0.0.1:7001> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:7002
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
# 省略
127.0.0.1:7001> exit
[root@dev-server-1 sentinel]# redis-cli -p 7002
127.0.0.1:7002> auth 123456
OK
127.0.0.1:7002> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=7001,state=online,offset=13642721,lag=1
# 省略
复制代码

能够看到slave 7002已经成功上位晋升为master(role:master),接收一个slave 7001的链接。此时查看slave2.conf配置文件,发现replicaof的配置已经被移除了,slave1.conf的配置文件里replicaof 127.0.0.1 6379 被改成 replicaof 127.0.0.1 7002。从新启动master,也能够看到master.conf配置文件中添加了replicaof 127.0.0.1 7002的配置项,可见大哥(master)下位后,再出来混就只能当当小弟(slave)了,三十年河东三十年河西。

3. 哨兵模式的优缺点

优势:

  1. 哨兵模式基于主从复制模式,因此主从复制模式有的优势,哨兵模式也有
  2. 哨兵模式下,master挂掉能够自动进行切换,系统可用性更高

缺点:

  1. 一样也继承了主从模式难以在线扩容的缺点,Redis的容量受限于单机配置
  2. 须要额外的资源来启动sentinel进程,实现相对复杂一点,同时slave节点做为备份节点不提供服务

Cluster模式

1. 基本原理

哨兵模式解决了主从复制不能自动故障转移,达不到高可用的问题,但仍是存在难以在线扩容,Redis容量受限于单机配置的问题。Cluster模式实现了Redis的分布式存储,即每台节点存储不一样的内容,来解决在线扩容的问题。如图

redis-cluster

Cluster采用无中心结构,它的特色以下:

  1. 全部的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽
  2. 节点的fail是经过集群中超过半数的节点检测失效时才生效
  3. 客户端与redis节点直连,不须要中间代理层.客户端不须要链接集群全部节点,链接集群中任何一个可用节点便可

Cluster模式的具体工做机制:

  1. 在Redis的每一个节点上,都有一个插槽(slot),取值范围为0-16383
  2. 当咱们存取key的时候,Redis会根据CRC16的算法得出一个结果,而后把结果对16384求余数,这样每一个key都会对应一个编号在0-16383之间的哈希槽,经过这个值,去找到对应的插槽所对应的节点,而后直接自动跳转到这个对应的节点上进行存取操做
  3. 为了保证高可用,Cluster模式也引入主从复制模式,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点
  4. 当其它主节点ping一个主节点A时,若是半数以上的主节点与A通讯超时,那么认为主节点A宕机了。若是主节点A和它的从节点都宕机了,那么该集群就没法再提供服务了

Cluster模式集群节点最小配置6个节点(3主3从,由于须要半数以上),其中主节点提供读写操做,从节点做为备用节点,不提供请求,只做为故障转移使用。

2. 部署演示

本示例基于Redis 5.0.3版。

Cluster模式的部署比较简单,首先在redis.conf中

port 7100 # 本示例6个节点端口分别为7100,7200,7300,7400,7500,7600 
daemonize yes # r后台运行 
pidfile /var/run/redis_7100.pid # pidfile文件对应7100,7200,7300,7400,7500,7600 
cluster-enabled yes # 开启集群模式 
masterauth passw0rd # 若是设置了密码,须要指定master密码
cluster-config-file nodes_7100.conf # 集群的配置文件,一样对应7100,7200等六个节点
cluster-node-timeout 15000 # 请求超时 默认15秒,可自行设置 
复制代码

分别以端口7100,7200,7300,7400,7500,7600 启动六个实例(若是是每一个服务器一个实例则配置可同样)

[root@dev-server-1 cluster]# redis-server redis_7100.conf
[root@dev-server-1 cluster]# redis-server redis_7200.conf
...
复制代码

而后经过命令将这个6个实例组成一个3主节点3从节点的集群,

redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7100 127.0.0.1:7200 127.0.0.1:7300 127.0.0.1:7400 127.0.0.1:7500 127.0.0.1:7600 -a passw0rd
复制代码

执行结果如图

redis-cluster-deploy

能够看到 7100, 7200, 7300 做为3个主节点,分配的slot分别为 0-5460, 5461-10922, 10923-16383, 7600做为7100的slave, 7500做为7300的slave,7400做为7200的slave。

咱们链接7100设置一个值

[root@dev-server-1 cluster]# redis-cli -p 7100 -c -a passw0rd
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:7100> set site blog.jboost
-> Redirected to slot [9421] located at 127.0.0.1:7200
OK
127.0.0.1:7200> get site
"blog.jboost"
127.0.0.1:7200>
复制代码

注意添加 -c 参数表示以集群模式,不然报 (error) MOVED 9421 127.0.0.1:7200 错误, 以 -a 参数指定密码,不然报(error) NOAUTH Authentication required错误。

从上面命令看到key为site算出的slot为9421,落在7200节点上,因此有Redirected to slot [9421] located at 127.0.0.1:7200,集群会自动进行跳转。所以客户端能够链接任何一个节点来进行数据的存取。

经过cluster nodes可查看集群的节点信息

127.0.0.1:7200> cluster nodes
eb28aaf090ed1b6b05033335e3d90a202b422d6c 127.0.0.1:7500@17500 slave c1047de2a1b5d5fa4666d554376ca8960895a955 0 1584165266071 5 connected
4cc0463878ae00e5dcf0b36c4345182e021932bc 127.0.0.1:7400@17400 slave 5544aa5ff20f14c4c3665476de6e537d76316b4a 0 1584165267074 4 connected
dbbb6420d64db22f35a9b6fa460b0878c172a2fb 127.0.0.1:7100@17100 master - 0 1584165266000 1 connected 0-5460
d4b434f5829e73e7e779147e905eea6247ffa5a2 127.0.0.1:7600@17600 slave dbbb6420d64db22f35a9b6fa460b0878c172a2fb 0 1584165265000 6 connected
5544aa5ff20f14c4c3665476de6e537d76316b4a 127.0.0.1:7200@17200 myself,master - 0 1584165267000 2 connected 5461-10922
c1047de2a1b5d5fa4666d554376ca8960895a955 127.0.0.1:7300@17300 master - 0 1584165268076 3 connected 10923-16383
复制代码

咱们将7200经过 kill -9 pid杀死进程来验证集群的高可用,从新进入集群执行cluster nodes能够看到7200 fail了,可是7400成了master,从新启动7200,能够看到此时7200已经变成了slave。

3. Cluster模式的优缺点

优势:

  1. 无中心架构,数据按照slot分布在多个节点。
  2. 集群中的每一个节点都是平等的关系,每一个节点都保存各自的数据和整个集群的状态。每一个节点都和其余全部节点链接,并且这些链接保持活跃,这样就保证了咱们只须要链接集群中的任意一个节点,就能够获取到其余节点的数据。
  3. 可线性扩展到1000多个节点,节点可动态添加或删除
  4. 可以实现自动故障转移,节点之间经过gossip协议交换状态信息,用投票机制完成slave到master的角色转换

缺点:

  1. 客户端实现复杂,驱动要求实现Smart Client,缓存slots mapping信息并及时更新,提升了开发难度。目前仅JedisCluster相对成熟,异常处理还不完善,好比常见的“max redirect exception”
  2. 节点会由于某些缘由发生阻塞(阻塞时间大于 cluster-node-timeout)被判断下线,这种failover是没有必要的
  3. 数据经过异步复制,不保证数据的强一致性
  4. slave充当“冷备”,不能缓解读压力
  5. 批量操做限制,目前只支持具备相同slot值的key执行批量操做,对mset、mget、sunion等操做支持不友好
  6. key事务操做支持有线,只支持多key在同一节点的事务操做,多key分布不一样节点时没法使用事务功能
  7. 不支持多数据库空间,单机redis能够支持16个db,集群模式下只能使用一个,即db 0

Redis Cluster模式不建议使用pipeline和multi-keys操做,减小max redirect产生的场景。

总结

本文介绍了Redis集群方案的三种模式,其中主从复制模式能实现读写分离,可是不能自动故障转移;哨兵模式基于主从复制模式,能实现自动故障转移,达到高可用,但与主从复制模式同样,不能在线扩容,容量受限于单机的配置;Cluster模式经过无中心化架构,实现分布式存储,可进行线性扩展,也能高可用,但对于像批量操做、事务操做等的支持性不够好。三种模式各有优缺点,可根据实际场景进行选择。

参考:

  1. blog.csdn.net/q649381130/…
  2. www.cnblogs.com/51life/p/10…
  3. www.cnblogs.com/chensuqian/…
  4. stor.51cto.com/art/201910/…

做者:空山新雨,一枚仍在学习路上的大龄码农
近期做者写了几十篇技术博客,内容包括Java、Spring Boot、Spring Cloud、Docker,技术管理心得等
欢迎关注做者微信公众号:空山新雨的技术空间,一块儿学习成长

微信公众号
相关文章
相关标签/搜索