Ardb集群设计(草案)

Update 2016-06

目前想法有改变,现在考虑基于raft + gossip实现类似redis cluster的功能,兼容后续的redis cluster client或者工具。

随着Ardb的Replication实现部分稳定下来,需要开始考虑如何实现集群的方案了。中秋的几天抽空思考了下相关设计,以下是初步设想,可以作为初步设计草案。

目标与约定

  • 兼容Redis的client的cluster部分 —— 意味着client端可以直接用redis的各种支持cluster的client实现,而无需单独开发
  • 集群节点间复制机制沿用现有异步实现,意味着在主从切换时存在丢数据的可能,这个无法避免;此约束需要特别指明,以防误解
  • 此分布式系统应该满足AP限制, 较弱的一致性约束
  • 需要支持大于1的备份;
  • 每个节点最多只有一个Slave,多备份情况下见后续的slave作为上一slave的slave串行相连
  • 数据分库存放 —— 数据按现有的库实现分区存放(和redis的slot实现不同), 支持0到0xFFFFFF, cluster中应该可以限制更小一些,比如16384(redis的取值); 分库存放对后续迁移数据时会比较容易,无需迭代所有底层数据
  • 分两部分实现:Cluster Manager以及Node Agent
  • 整体基于zookeepr,Cluster Manager用python实现,基于kazoo; Node Agent直接基于zookeeper的c client库,集成到Ardb实例中。
  • 集群拓扑关系存于zookeeper中,各个节点同步获取
  • 集群环境原则上只支持单key的命令,支持的命令参考redis集群支持情况

Cluster Manager

功能

  • 新增节点发现
  • 宕机节点摘除

内部逻辑实现

  • Cluster Manager启动后第一步, 先获取分布式锁/ardb_cluster/global_lock;若获取到进行下一步, 否则继续等待获取 —— 用于保证同时只有一个管理程序在运行
  • 获取到唯一锁之后,先获取节点/ardb_cluster/active_nodes下的所有子节点, 节点为每个Node Agent连上zookeeper后创建的Ephemeral节点,节点名为”Host:Port”格式;节点同时携带一份json格式数据,有节点的servicekey等信息。
  • 同时,Cluster Manager可以从/ardb_cluster/cluster_info下获取当前集群拓扑信息,若在内存中存在该信息,则无需到ZK上获取
  • 集群拓扑信息大致可以用一个Map表示,key为servicekey, value为一个node list + 数据分库列表信息
  • Cluster Manager将获取到的节点列表和在内存中的集群拓扑比较,分别得到恢复节点集合/新增节点集合/删除节点集合
  • 后续将分别讨论恢复如何处理节点/删除节点/新增节点
  • 处理完毕后,等待1分钟,继续从第二步检查获取/ardb_cluster/active_nodes开始,如此反复

节点恢复

  • 若节点对应的拓扑信息中当前node list的长度等于配置的数据备份数, 则作为新增节点处理(需要考虑servicekey冲突问题)
  • 否则直接将该节点作为node list中最后一个无slave节点的slave,调用redis命令slaveof <ip> <port>即可

节点删除

  • 根据节点对应的拓扑信息中找到该节点的master/slave
  • 若都存在,则需要将slave作为自己master的slave,调用redis命令到自己slave上执行slaveof <mymaster_ip> <mymaster_port>即可
  • 若master不存在,则说明该节点为该数据分区的master,直接将自己slave提升为master, 执行redis命令slaveof no one
  • 若slave不存在,意味是最后一个备份节点,无需任何处理

节点新增

  • 若当前拓扑信息中某数据分区缺少一个节点作为备份,直接加入到该分区,作为最后一个备份加入
  • 否则,判断是否需要作为集群扩容处理,判断准则为至少有指定备份数机器待加入集群
  • 未达到集群扩容处理标准,将该节点状态置为CLUSTER_PENDDING
  • 达到集群扩容处理标准后,先将该批机器master/slave关系确定
  • 然后计算需要迁移的数据分区,调用相应命令迁移数据到该批机器上(需要更详细方案);数据状态为DATA_MIGRATING
  • 数据迁移完成后, 该节点状态置为CLUSTER_READY, 数据对应状态为DATA_READY

一些待完善的思路

  • servicekey冲突 —— 新增节点的servicekey与现有集群中冲突
  • 数据迁移的具体过程实现
  • 数据迁移时间可能很长,会阻塞Cluster Manager逻辑,考虑是否支持并行处理(数据迁移中,同时例行监测集群事件), 支持并行可能遇到更复杂的情况

总结

  • 最复杂的就是集群扩容问题,其它的相对非常简单

Node Agent & Ardb

内部逻辑实现

  • Node Agent集成在Ardb实例内部,当配置项’cluster-enabled’设置为’yes’时启动
  • 启动后,连接zookeeper, 在节点/ardb_cluster/active_nodes下创建Ephemeral节点, 节点名为’host:port’形式, 携带数据为json格式数据
  • 同时获取并watch节点/ardb_cluster/cluster_info内容,与当前集群拓扑内容保持同步; 根据集群拓扑内容在内存中构建集群拓扑与数据路由表
  • 将redis命令中的key通过制定hash算法得到一个hash值,然后根据集群拓扑表判断此请求应该在哪台机器上
  • 若当前机器状态非”CLUSTER_READY”,则对所有请求恢复一致错误
  • 若在当前机器上,处理该请求
  • 若在其它机器上,根据路由表回复重定向应答”-moved host:port”

一些待完善的思路

  • 数据迁移如何实现
  • 是否引入和Redis实现类似的 -ASK 重定向
  • 是否需要区分读写请求,进而将读请求转至slave处理?(存在数据一致性问题, 即master写入后,从slave读)
  • 是否需要和Cluster Manager直接交互?提供扩展redis命令?如servicekey reset

总结

  • 数据迁移的实现需要更详细的方案