Redis集群

Redis集群

Redis Cluster是redis的分布式解决方案,在3.0版本正式推出后,有效地解决了Redis分布式方面的需求。Redis分布式的方案一般由两种:

  1. 客户端分区方案,优点是分区逻辑可控,缺点是需要自己处理数据路由,高可用,故障转移等问题;
  2. 代理方案,优点是简化客户端分布式逻辑和升级维护便利,缺点是加重架构部署复杂性和性能损耗
    现在官方为我们提供了专门的集群方案:Redis Cluster,采用Hash分区

数据分布

数据分区理论基础

分布式数据库首先要解决的问题是整个数据集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,每个节点负责整体数据的一个子集。
常见的数据分区规则有hash分区和顺序分区两种
哈希分区和数据分区的特点对比

分区方式特点代表产品
hash分区离散性好,数据分布业务无关,无法顺序访问Redis Cluster,Cassandra,Dynamo
顺序分区离散度易倾斜,数据分布业务相关,可顺序访问Bigtable,HBase,Hypertable

Hash分区常见有三种:

  1. 节点取余分区
    定义:根据节点数量N使用公式hash(key)%N,计算出出hash值,用来决定数据映射到哪个节点上
    优点:简单,常用于数据库的分库分表规则,一般采用预分区的方案,提前根据数据量规划好分区数。
    存在问题:当节点数量变化,需要扩容或者收缩节点,数据节点映射关系需要重新计算会导致数据的重新迁移
    注意事项:扩容时采用翻倍扩容,避免数据全量被打乱导致全量迁移
  2. 一致性Hash分区
    定义:为每个系统分配一个token,范围一般在0~2的32次方,这些token构成一个hash环,数据读写执行节点查找操作时,先根据key计算hash值,然后顺时针找到第一个大于等于该hash值的token节点
    优点:加入和删除节点只影响哈希环中相邻的节点,对其他节点无影响
    存在问题:
    1) 加减节点会造成哈希环中部分数据无法命中,需要手动处理或者或略这部分数据,因此一致性hash常用于缓存场景
    2)当使用少量节点时,节点变化将会大范围影响哈希环中数据映射,因此这种方式不适合少量数量节点的分布式方案
    3)普通的一致性hash分区在增加节点时需要增加一倍或者减去一般节点才能保证数据和负载的均衡
  3. 虚拟槽分区
    定义:虚拟槽分区巧妙地使用了哈希分区空间,使用分散性良好的哈希函数把所有数据映射到一个固定范围的整数集合中,整数定义为槽
    这个范围一般远远大于节点数,比如Redis Cluster槽的范围是0~16383
    槽是集群内数据管理和迁移的基本单位,采用大范围的主要目的是为了方便数据拆分和集群扩展

Redis数据分区

Redis Cluster采用了虚拟槽分区,所有的键根据哈希函数映射到0~16383整数槽内,计算公式:slot=CRC16(key)&16383。每一个节点负责维护一部分槽以及槽所映射的键值数据

  • Redis虚拟槽分区特点
  1. 解耦数据和节点之间的关系,简化了节点扩容和收缩的难度
  2. 节点自身维护槽的映射关系,不需要客户端或者代理服务维护槽分区元数据
  3. 支持节点,槽,键之间的映射关系,用于数据路由,在线伸缩等场景

集群功能限制

Redis集群相对于单机存在一些限制

  1. key批量操作支持有限。如mset,mget,目前只支持具有相同slot值的key执行批量操作
  2. key事务操作支持有限。只支持多key在同一节点上的事务特性
  3. key作为数据分区的最小粒度。因此不能将一个大的键值对象如hash,list等映射到不同的节点上
  4. 不支持多数据库空间。单机模式下可以支持16个数据库,集群模式下只能使用一个数据库空间,即db 0
  5. 复制结构只支持一层,从节点只能复制主节点,不支持嵌套树状复制结构

节点通信

通信流程

分布式存储中需要提供维护节点元数据信息的机制,元数据是指:节点负责哪些数据,是否出现故障等状态信息
常见元数据维护方式分为两种:集中式和P2P方式
Redis采用的是P2P的gossip协议。Gossip协议工作原理就是节点彼此不断通信交换信息,一段时间后所有节点都会知道集群完整的信息
通信说明:

  1. 集群中每个节点都会单独开辟一个TCP通道,用于节点之间彼此通信,通信端口号在基础端口号上加10000
  2. 每个节点在固定周期内通过特定规则选择几个节点发送ping消息
  3. 接收到ping消息的节点用pong消息进行响应

Gossip消息

Gossip协议的主要职责是信息交换。信息交换的载体是节点彼此发送的Gossip消息。
常见Gossip消息有:ping,pong,meet,fail等

meet:用于通知新节点加入。消息发送者通知接受者加入到当前集群,meet消息通知正常完成后,接受节点会加入到集群中进行周期性的ping,pong消息交换
ping:集群内交换最频繁的消息,集群内每个节点每秒向多个其他节点发送ping消息,用于检测节点是否在线和交换状态信息,ping消息发送封装了自身节点和部分其他节点的状态数据
pong:当接收到ping,meet消息时,作为响应消息回复给发送方确认消息正常通信。pong消息内部封装了自身状态数据,节点也可以向集群内广播自身的pong消息来通知整个集群对自身状态进行更新
fail:当节点判定集群中另一个节点下线时,会向集群中广播一个fail消息,其他节点收到fail消息之后把对应节点更新为下线状态

节点选择

Redis集群节点通信采用固定频率(定时任务每秒执行10次)。因此节点每次选择需要通信的节点列表十分重要,选择过多,信息交换及时但是成本较高;选择过少会导致节点彼此信息交换不及时,影响故障判断,新节点发现等需求速度。因此Redis集群的Gossip协议需要兼顾信息交换实时性和成本开销

  1. 选择发送消息的节点数量
    集群内每个节点维护定时任务默认每秒执行10次,每秒随机选择5个节点找出最久没有通信的节点发送ping消息,用于保证Giossip消息交换的随机性。每100毫秒都会扫描本地节点列表,如果发现节点最近一次接收pong消息时间大于cluster_node_timeout/2,则立刻发送ping消息,防止该节点消息太长时间未更新。
  2. 消息数据量
    每个ping消息的数据量体现在消息头和消息体中,其中消息头主要占用空间的字段是myslots[CLUSTER_SLOTS/8],占用2KB,这块空间相对稳定。
    消息体中会携带一定数量的其他节点信息用于信息交换。

集群伸缩

伸缩原理

Redis集群可以在不影响集群对外提供服务的情况下,为集群添加节点进行扩容也可以下线部分节点进行缩容

以上图为例进行说明集群节点上下线,原理可抽象为槽和对应数据在不同节点之间灵活移动。

上图为初始状态,把一部分槽和数据迁移到新的节点6385上,如下图

扩容集群

Redis集群扩容操作可分为如下步骤

  • 准备新节点
  • 加入集群
  • 迁移槽和数据
  1. 准备新节点
    提前把新节点运行在集群模式下,启动两个节点6385,6386
    启动后的节点作为孤儿节点,并没有和其他节点进行通信,集群结构
  2. 加入节点
    新节点依然采用cluster master命令把6385和6386加入到现有集群中
    先加入后的节点开始都是主节点状态,但是没有负责的槽,不能接收任何的读写操作,对于新节点有两种选择:
    1 它迁移槽和数据实现扩容
    2 作为其他主节点的从节点负责故障转移
  3. 迁移槽和数据
    加入集群后需要为新节点迁移槽和数据,槽的迁移过程中集群可以正常提供读写服务。
    迁移过程的流程
    (1)槽迁移计划
    槽是Redis集群管理数据的基本单位,首先需要为新节点制定槽的迁移计划,确定哪些槽需要迁移到新节点,迁移计划需要保证每个节点负责相似数量的槽,从而保证各节点均匀分布。

    (2)迁移数据
    数据迁移过程是逐个槽进行的,每个槽数据迁移的流程如下
    1)对目标节点发送cluster setslot {slot} importing {sourceNodeId}命令,让目标节点准备导入槽的数据
    2)对槽节点发送cluster setslots {slot} migrating {targetNodeId}命令,让源节点准备迁移出槽的数据
    3)源节点循环执行cluster getkeysinslot {slot} {count}命令,获取count个属于槽{slot}的键
    4)在源节点上执行migrate {targetIp} {targetPort} “” 0 {timeout} keys {keys…} 命令,把获取到的键通过流水线(pipeline)机制批量迁移到目标节点,对于3.0.6版本以前只能单个迁移
    5)重复执行步骤3和步骤4直到槽下所有的键值数据迁移到目标节点
    6)向集群内所有主节点发送cluster setslot {slot} node {targetNodeId} 命令,通知槽分配给目标节点。
    (3)添加从节点
    把6386作为6385的从节点,从而保证整个集群的高可用,使用cluster replicate {masterNodeId}命令为主节点添加从节点,注意在集群模式下slaveof添加从节点操作不再支持。

收缩集群

收缩集群意味着减小规模,需要从现有集群中安全下线部分节点

流程说明
1)首先需要确定下线节点是否有负责的槽,如果有,需要把槽迁移到其他节点,保证节点下线后整个集群槽节点映射的完整性
2)当下线节点不再负责槽或者本身是从节点,就可以通知集群内其他节点忘记下线节点,当所有的节点忘记该节点后就可以关闭了。

  1. 下线迁移槽
    下线节点需要把自己负责的槽迁移到其他节点,原理与之前扩容迁移槽过程一样,下线节点槽迁出后,剩下的步骤需要让集群忘记该节点
  2. 忘记节点

    集群内的节点不停地通过Gossip消息彼此交换节点状态,因此需要通过一种健壮的机制让集群内所有节点忘记下线的节点。Redis提供了cluster forget {downNodeId}命令实现该功能。
    当节点接收到cluster forget {downNodeId}命令后,会把nodeId指定的节点加入禁用列表中,在禁用列表中的节点不再发送Gossip消息,禁用列表有效期为60s,超过60s节点会再次参与消息交换。也就是说当第一次forget命令翻出后,我们有60s的时间让集群内的所有节点忘记下线节点。
    线上不建议直接使用cluster forget 命令下线节点,需要跟大量节点命令交互,实际操作过于繁琐并且容易遗漏forget节点。建议使用redis-teib.rb del-node {node:port} {downNodeId}命令。
    del-node帮我们实现安全下线,当下线主节点具有从节点时,需要该从节点指向到其他拥有最少从节点的主节点,因此对于主从节点都下线的情况,建议先下线从节点,在下线主节点,防止不必要的全量复制。
  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值