0%

Redis分布式相关

Redis支持多机多实例部署,通过多实例部署,可以保证服务不受到单个Redis示例宕机的影响,实现服务的数据的可靠性和高可用,下面结合代码和底层原理,加深对于Redis多机部署的理解,主要内容来自Redis设计与实现

主从复制

Redis的主从复制在我看来较为轻量级,由从节点向主节点申请成为从节点,主节点不断向从节点发送数据,实现同步,与Mysql主从复制不同的是,即使在同步前主从状态不一致,同步后也会变为一致。

通过replicaof命令可制定当前Redis实例为对应实例的从机关系,重启即失效:

1
2
3
4
# replicaof 主机IP 端口
replicaof 192.168.1.1 6379
# 取消作为从机
replicate no one

或者在配置文件中永久开启:

开启主从备份后,可以在任意Redis实例上通过info replication命令查看同步信息

若主机配置了requirepass,需要在配置文件中添加对应连接密码

1
2
3
4
# 临时设置
config set masterauth <password>
# 配置文件中添加
masterauth <password>

其他的相关注意事项有:

  1. 参与主从复制的任意Redis实例宕机,其他Redis实例仍保持原来的身份,导致如果主机宕机丢失数据,在空数据的条件下启动,会导致所有从机同步为空(这一点有悖于复制的初衷)。
  2. Redis主从复制采用异步复制策略,主机向从机发送操作后并不会等待操作返回。
  3. 对于带有过期时间(TTL)的键值对,从库不会主动删除,等待主机删除后向从机发送同步删除命令,只是根据本地逻辑时钟判断是否向客户端返回空。

实现原理

主从同步的主要操作按照连接状况的不同分为以下几种情况:

  1. 主机与从机正常连接。主机不断地将写操作发送到从机,从机执行操作实现状态同步,这个过程为异步的,主机并不等待从机返回。
  2. 从机与主机完全不同步状态。主机接收到请求后,启动后台线程持久化当前状态到RDB文件中,写入完毕后,发送给从机读取,实现状态同步。
  3. 从机与主机处于部分同步状态。主机根据两者数据之间的差距,发送从机缺失的操作,从机读取执行,实现状态同步,例如从机断线,网络分区等场景导致。

老版本Redis支支持情况2对应的完全同步(sync)操作,对于情况3同样采用完全同步,这导致即使从机只由于网络波动缺失了主机的部分操作,而主机还是需要重新生成RDB文件,造成了效率的下降。为了解决上述问题,新版本Redis引入了部分同步操作(psync),在上述场景中主机能够识别从机缺失部分,直接发送缺失部分操作。

为了支持部分同步操作,每个主机上添加三个数据结构:

  1. 复制ID(replication ID):主机启动时初始化一个独一无二的ID(单机自己为主机),用来标识本机数据,同时每个实例记录自己的上一个复制ID,原因在官方文档中描述了。
  2. 主机复制偏移量(replication offset):每当主机需要从机发送写操作时,在复制偏移量上加上操作对应发送的数据字节长度,可以理解为逻辑上的操作时间戳
  3. 主机复制积压缓冲区(replication backlog):固定长度的先进先出缓冲区,缓存一定数量的主机向从机发送的最新复制数据,主机在向从机发送复制数据时,也会写入backlog。

从上面三个数据结构,不难想到Redis是如何实现部分同步操作(psync):主机复制ID用来比较从机的数据是否与本机来自一个”数据”,复制偏移量用来判断从机相较于主机缺失多少数据,复制挤压缓冲区存历史数据代表主机能够恢复的数据。

  1. backlog 长度 > 主机 replication offset - 从机 replication offset,主机携带了所有从机缺失的数据,进行部分同步(psync)
  2. backlog 长度 < 主机 replication offset - 从机 replication offset,主机无能为例,只能完全同步(sync)

replication ID在区别“数据”版本上具有重要作用,其变化时机为:

  1. 实例重新启动时
  2. 当一个从实例被设置为主实例时

从实例会主动向主服务器发送心跳包,实现检测主服务器网络状态,告知主服务器自身的复制偏移量。

哨兵模式

在读写分离的主从复制模式下,如果主节点失效,从节点并不会自己重新选出新的主节点,导致系统陷入只读状态,因此Redis引入了哨兵模式:一种特殊的Redis服务器,用来监控主节点的状态,当主节点失效时,该服务器会选择特定的从服务器成为新的主服务器,其他服务器(包括恢复后的老主服务器)修改为新主的从服务器。

Sentinel模式实现的基本原理如下(简单概括):

  1. Sentinel是运行在特殊模式下的Redis服务器,通过读取指定的配置文件,获悉监视的主服务器IP以及端口。
  2. 通过向主服务器发送请求,获取所有的从服务器信息,保存完整的主从服务器信息。
  3. Sentinel周期性的向所有主从服务器发送INFO命令,当主服务超过一定时间未有效响应时,即认为主服务器失效,当足够多的Sentinel认为主服务下线时,进入故障切换阶段(基于Raft选主多的Sentinel)。
  4. 主Sentinel选择一个从服务器发送replica no one命令,其他发送replicaof命令,完成新的主服务器切换。
  5. 失效的旧主服务器上限后,主Sentinel也会发送replicaof命令

使用

启用Sentinel相关配置项如下:

1
2
3
4
5
6
# 监控的主Redis服务器,最后一个
sentinel monitor mymaster 127.0.0.1 6379 2
# 判断下线的时间阈值
sentinel down-after-milliseconds mymaster 60000
# 故障转移阶段超时时间阈值
sentinel failover-timeout mymaster 180000

指定配置文件的方式进行启动:

1
2
redis-sentinel /path/to/sentinel.conf
redis-server /path/to/sentinel.conf --sentinel

启动成功后,输出监视信息(一主一从):

手动关闭Salve,查看输出:

启动Slave后,关闭Master查看输出:

  • 显示完成切换

在Slave查看主从备份信息,发现切换成功:

集群

Redis集群是Redis的分布式数据库方案,支持数据分片和复制,实现数据库的水平扩展,一个典型的Redis集群组成如下:

  1. 一系列主服务器,存储部分键值对,提供读写服务。
  2. 一系列从服务器,复制主服务器数据,在主服务器失效时替代主服务器提供服务。

键空间划分为共16384个槽(slot),将槽分别存储在不同的主服务器上,实现所谓的分片(Sharding),采用CRC16算法实现key到slot的映射,其转化计算方式为:

  • Node A contains hash slots from 0 to 5500.
  • Node B contains hash slots from 5501 to 11000.
  • Node C contains hash slots from 11001 to 16383.

Redis集群采用完全去中心化的实现方式,客户端可访问任意集群服务器,当访问操作的键值对不在当前服务器(槽不在当前服务器或者正处于槽迁移阶段)上时,当前服务器返回对应key所在服务器位置,并指导客户端重定向到对应服务器。

其他包括故障检测、故障恢复等均通过节点间的消息传递实现,当主服务器宕机,基于Raft算法从多个从服务器中选择成为新的主服务器

使用

按照官网教程的方式,采用一台虚拟机多个Redis进程的方式,学习Redis集群的配置和使用。

首先构建六个配置文件,下面为最小配置内容,不同配置文件需要修改端口号,集群配置文件名

1
2
3
4
5
port 7000 # 不同节点不同
cluster-enabled yes
cluster-config-file nodes.conf # 不同节点不同
cluster-node-timeout 5000
appendonly yes

加载配置文件,启动对应Redis实例

初始化集群,完成集群搭建(六个,三个主,三个从)

1
2
3
redis-cli --cluster create 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 127.0.0.1:7006 \
--cluster-replicas 1

操作集群

  1. 访问不在当前节点上的Key

  2. 查看指定键所属的slot

  3. 查看集群信息

关闭节点7003后,查看集群状态

  • 可以看出,此时的7003状态为Failed,其从节点7005成为新的主节点