Redis高可用详解

1,248 阅读10分钟

Redis高可用详解.png Redis时一款高性能的key-value型数据库,在项目中的应用非常广泛,每一个开发者都应该掌握好这项技能。

在上篇Redis文章中我们介绍了Redis的持久化相关的内容,持久化只是保证了服务宕机时数据的可靠性,在分布式架构中单点问题也是必须要考虑到的一个点。Redis作为一款被广泛使用的组件肯定也提供了对应的解决方案,在这篇文章中,我们详细的介绍下Redis高可用相关的内容,今天的文章包括如下几部分:

  • 主从模式
  • 哨兵模式
  • cluster集群模式

一、主从模式

主从的高可用方式相信大家都听过,很多中间件都是采用的这种方式。但是Redis的主从模式当主库挂了,不会自动的选举新的主库,如果想要实现自动的故障转移可以采用后面介绍的哨兵或cluster部署的方式。

这个模式的部署方式也很简单,只需在从库的配置文件中增加如下配置项即可

replicaof <masterip> <masterport>

这种方式增加了数据的副本,实现了数据的读写分离,主库可以处理写请求和读请求,从库只处理读请求,新增的数据需要从主库同步,其架构如下:

主从复制架构.png

主从复制过程

Redis的主从复制会存在全量复制和增量复制两种情况,其大致流程如下:

主从复制首次或非首次.png

1、建立连接,当一个从库启动后,会先跟主库建立连接,当有从库接入后在主库有两个比较重要的buffer区域,如下

  • replication buffer 从库建立连接后,Redis会分配一个内存buffer用于数据交互,Redis主库收到写请求后,会将数据写入到这个buffer中,然后再通过网络发送给从库
  • repl_backlog_buffer 这个buffer区域和replication buffer的区别在于,replication buffer会为每一个连接分配一个,而该buffer区域只存在一个。该区域是一个环形缓冲区,主库的写请求发送给从库后也会在该区域记录一份。当有从库断开重连后会根据偏移量从该区域获取到需要同步的增量数据。

2、建立连接后,从库会发送psync请求,请求参数有如下两个

  • runId 每个Redis实例启动后都会自动生成一个id,用来标记该实例,首次同步时从库不知道这个id是什么,会传递"?",这个值每次启动均会变更,在4.0版本之后使用的是master_replid
  • offset 同步的偏移量,用来记录同步的数据位置 首次同步时传递-1

3、主库收到从库发送的psync请求后,如果是首次连接会返回给从库自己的runId和复制偏移量,从库会进行记录

4、判断是否需要全量复制,需要全量复制的话主库会执行一次bgsave操作,生成RDB文件,如下情况会进行全量复制

  • 请求传递的runId和主库的runId不一致,首次启动或者切换主节点是会存在该情况
  • offset的值在repl_backlog_buffer区域中不存在,该区域是一个环形缓冲区,因此会存在覆盖的情况

5、将RDB文件或者增量数据发送给从库,当从库接受到是RDB文件时,会清空自身当前的数据然后执行收到的RDB文件,用来保证和主节点的数据一致性

6、发送replication buffer区域中的数据给从库

二、哨兵模式

在上面介绍的主从模式只是在一定程度上保证了Redis的高可用,例如当一个从节点宕机了,Redis的读写操作均不受影响;但当主节点宕机时,我们的Redis服务便无法进行写操作了。虽然主从模式解决了读写分离及数据备份的问题,但并没有解决自动切换master节点的问题,因此便有了本节的哨兵模式及后面的cluster集群模式。

sentinel是单独的服务,提供了检测、通知、自动故障转移、配置提供的功能,保证了Redis的高可用性

  • 监控:监控各Redis节点的状态
  • 通知:当Redis节点发生故障时,能够通知给其他节点及sentinel节点
  • 自动故障转移:当主节点宕机时,可以自动选择一个从节点升级为主节点
  • 配置提供:客户端可以通过sentinel获取到master实例地址

使用哨兵模式部署的架构图如下:

哨兵模式架构.png

sentinel服务启动

sentinel是一个单独的服务,为了避免这个服务产生单点故障,因此这个服务通常也要进行集群部署。

配置sentinel.conf文件,该文件和redis.conf在同一目录下(安装包的根目录)

port 26379
# 是否后台启动
daemonize no
# 自定义名称 sentinel monitor <name> <Redis服务主节点ip> <Redis服务主节点端口> <最小投票数>
sentinel monitor mymaster 127.0.0.1 6379 2
# 多长时间联系不到 认为关闭
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000

修改完成后进入src目录执行如下命令启动sentinel服务

./redis-sentinel ../sentinel.conf

使用哨兵模式的部署方式,客户端不再直接连接Redis服务,而是连接哨兵服务,从哨兵服务中获取到主节点的信息,示例如下:

哨兵获取主节点.png

哨兵故障转移流程

哨兵节点会通过PING命令检测各个Redis节点的状态,哨兵发现主节点故障并进行转移的过程如下:

1、当一个sentinel节点在一定时间内(down-after-milliseconds配置的值)没有收到Redis节点的正确响应便认为该节点出现了问题,此时会将该节点标记为主观下线。

2、如果该Redis节点为主库,该sentinel会去询问其他哨兵节点其状态,如果超过了配置的投票数量(monitor中配置的投票数)的sentinel节点均认为其有问题,此时,便认为主库真的出问题了,进而标记为客观下线。

3、sentinel集群投票选出一个进行故障转移的sentinel节点。

4、选出一个新的主库进行故障转移。

新的主库的选择

新的主库的选择中需要考虑的信息如下:

  • 与主节点断开连接的时间 与Redis主库断开连接的时长超过了一定的阈值(down-after-milliseconds * 10)的从库不具有被选择的资格。
  • 优先级 可以通过slave-priority配置项设置Redis节点的优先级,优先级高的节点会被选择为新的主库。
  • 数据的复制偏移量 优先级都相同的情况下会比较各个从库中的slave_repl_offset的值,与主库中master_repl_offset最接近的节点被选择为新的主库。
  • 示例id 各个节点的优先级和复制偏移量均相同的情况下,id号最小的节点被选择为新的主库。

三、cluster集群模式

在上面介绍的哨兵模式虽然解决了故障自动转移的问题,但每个Redis节点均存储的是全部的数据量,当数据量过大时也会影响到Redis性能,cluster集群模式便可很好的解决这个问题,这种部署方式会将数据进行分片部署到不同的节点上。

集群搭建

cluster集群搭建需要至少需要6个Redis服务节点,将其分为三组,每组有一主一从两个节点。

由于我这里只有一台电脑,因此我在这里分别使用6381、6382、6383、6384、6385、6386六个端口,主从关系如下:

  • 6381主 6384从
  • 6382主 6385从
  • 6383主 6386从
# 进入Redis根目录
cd /Users/xiehua/develop/redis-cluster
# 创建相关文件夹
mkdir -p redis_63{81,82,83,84,85,86}/{conf,pid,logs}
# 复制redis.conf文件
cp redis.conf redis_6381/conf/

修改redis_6381/conf/redis.conf文件,复制到其他几个文件夹并修改对应的内容,示例如下:

# 端口
port 6381
​
daemonize yes
​
pidfile /Users/xiehua/develop/redis-cluster/redis_6381/pid/redis_6381.pid
logfile /Users/xiehua/develop/redis-cluster/redis_6381/logs/redis_6381.log
# 存储目录
dir /Users/xiehua/develop/redis-cluster/redis_6381
# RDB文件名
dbfilename redis_6381.rdb
​
# 是否以集群模式启动
cluster-enabled yes
# 生成的集群节点的配置文件名
cluster-config-file nodes_6381.conf

配置文件修改完毕后依次启动这6个服务并将这6个节点加入一个集群

./redis-server ../../redis_6381/conf/redis.conf
./redis-server ../../redis_6382/conf/redis.conf
./redis-server ../../redis_6383/conf/redis.conf
./redis-server ../../redis_6384/conf/redis.conf
./redis-server ../../redis_6385/conf/redis.conf
./redis-server ../../redis_6386/conf/redis.conf
​
# 连接6381的服务,并将服务加入集群中
./redis-cli -p 6381
cluster meet 127.0.0.1 6382
cluster meet 127.0.0.1 6383
cluster meet 127.0.0.1 6384
cluster meet 127.0.0.1 6385
cluster meet 127.0.0.1 6386
​
# 查看节点信息
cluster nodes

加入集群.png

执行完成上面的命令后会看到截图中的所有节点信息,由于没有建立主从关系,目前6个节点均为主节点,依次连接6384、6385、6386三个服务,同对应的服务建立主从关系,如下图:

建立主从关系.png

建立好主从关系后再次查看节点信息,我们可以看到现在的节点有三主三从。

至此还有最后一步就是需要给节点分配虚拟槽,在Redis中共有16384个虚拟槽,从0到16383,分配命令如下

./redis-cli -p 6381 
cluster addslotsrange 0 5461
​
./redis-cli -p 6382 
cluster addslotsrange 5462 10922
​
./redis-cli -p 6383 
cluster addslotsrange 10923 16383

虚拟槽分配完毕后,集群的状态会变成ok,至此我们的cluster集群便搭建完成了,再次查看集群信息和集群节点信息如下:

集群搭建完成.png

虚拟槽

虚拟槽是cluster集群中一个关键的概念,当我们搭建集群时需要为Redis实例分配虚拟槽,操作数据时会根据key计算出对应的虚拟槽,然后找到对应的分片节点。在新增或去除机器时均需重新分配虚拟槽。

四、总结

今天的文章就这些内容了,希望通过本文能让大家了解到redis提供了哪些高可用方案。

  • 主从模式,可以解决读写分离的问题,但无法实现故障自动转移。
  • 哨兵模式,实现了主节点故障自动转移,但没有对数据进行分片。
  • cluster集群模式,即可故障自动转移,也可以对数据进行分片。
  • 具体选择哪种模式根据具体的业务来分析即可,个人感觉主从模式可以满足大部分的中小项目的需求了,对于数据量比较大的项目来说,可以使用cluster集群模式。
  • Redis虽然提供了主从复制的功能,但其并不会保证数据的一致性,可能会存在数据的丢失。

欢迎大家关注我的公众号:【Bug搬运小能手】