聊聊 Doris 的架构设计 - 上篇

用户头像
Jerry Tse
关注
发布于: 2020 年 07 月 15 日

Doris定位是一个海量分布式KV存储系统,其设计目标是支持中等规模高可用可伸缩的KV存储集群。



1. 概念



三个角色

  • 应用程序服务器:是存储服务器的客户,上面部署着存储服务器客户端程序。

  • 数据存储服务器:负责存储数据,响应应用程序的数据操作请求。

  • 管理中心服务器:

  • 为应用程序提供集群地址配置信息

  • 负责对存储集群健康心跳监测、集群扩容、故障转移、



序列

序列可以理解为存储集群中子集群的概念。我们将副本存储于不同的序列的不同节点中,一份数据多地备份,提升数据可靠性



  • 写入数据的时候,客户端根据集群配置和应用可用性级别(副本数)在每一个序列中使用路由算法获取一台服务器,同时并发写入这些服务器。

  • 读取的时候随机选择一个序列,根据相同的路由算法获取节点,即可读取。

public List<StoreNode> findNodes(OperationEnum type, int copyCount, String key) throws DorisRouterException {
if (rlc.getVpmrList() == null || rlc.getVpmrList().isEmpty()) {
log.error("Current RouterListContainer is:" + rlc);
throw new DorisRouterException("There is no store node.");
}
if (copyCount > rlc.getVpmrList().size()) {
log.error("Current RouterListContainer is:" + rlc);
throw new DorisRouterException("There is only " + rlc.getVpmrList().size() + " sequence, Can't support "
+ copyCount + " copy count!");
}
List<StoreNode> snList = new ArrayList<StoreNode>();
for (int i = 0; i < copyCount; i++) {
if (type.equals(OperationEnum.READ) || type.equals(OperationEnum.WRITE)
|| type.equals(OperationEnum.MULTIREAD)) {// 写操作一定要取得一个node,读操作只有在可读时才取node
int logicId = rlc.getVpmrList().get(i).getNodeByKey(key);
List<List<StoreNode>> mainNodeList = rlc.getMainStoreNodeList();
List<StoreNode> seqNodeList = mainNodeList.get(i);
StoreNode sn = seqNodeList.get(logicId);
snList.add(sn);
}
}
if (snList.isEmpty()) {
throw new DorisRouterException("No store node can be used!");
}
//。。。
}

以上为Doris客户端选择节点的源码。通过源码可见:

  • 我们设置的集群序列数量要大于等于节点副本数,否则找不到对应序列中的节点(序列类型配置与StoreNode中)

  • 每一个序列都可以有单独的路由算法



2. 路由算法

路由算法是一个分布式系统的核心,影响着扩容、故障转移等方面。



Doris有Client端负责路由算法,支持两种路由算法

  • 一致性哈希算法:ConsistentHashRouteAlglorithm

  • 虚拟机节点物理节点映射算法(我自己起的名字):VpmRouterAlgorithm(默认使用的路由算法)



一致性哈希算法我们不在赘述,我们关注一下VpmRouterAlgorithm(简称映射算法)。



  • 算法固定了虚拟节点总数(图中为6个)

  • 使用余数哈希计算计算key落入哪一个虚拟节点,hash(key)%6。

  • 建立物理节点和虚拟节点的对应关系,将虚拟节点均匀分配给物理节点。(算法关键)

  • 扩容操作相当于将虚拟节点重新分配。pn1和pn2分别出让一个虚拟节点vn2和vn6给新增节点pn3。只需迁移pn1中vn3对应数据和pn2中vn6对应数据进pn3即可。

  • 因为使我们通过算法规则分配的虚拟节点物理节点关系,因此算法数据分布均衡向要优于一致性哈希(随机分配虚拟节点)。

这里要注意,Doris VpmRouterAlgorithm 路由算法的虚拟节点的最大数量为一万,因为此算法物理节点数目不能超过虚拟节点数据,所以集群最大节点容量也是一万个。符合Doris中等规模数据量的设计目标。



3. 故障转移

3.1 故障分类

  • 瞬时故障:主要由网路瞬间中断、服务器内存回收或瞬时线程繁忙等原因引起。这类故障通常可以自愈且恢复时间很短。

  • 临时故障:服务器无法恢复运行,需要人工干预。

  • 永久故障:服务器损坏,需要人工干预。



3.2 高可用保证

因为采用数据副本方式对数据提供冗余,且冗余存储于不同序列的不用节点中。所以某一节点发生故障,其他未故障节点可以暂时代替故障节点工作。保证了系统高可用。



接下来我们以写入副本数为两个的场景下,介绍系统如何故障转移并修复。

3.3 瞬时失效向临时故障转移

  • 瞬时故障通常可以短时间自愈,所以客户端采用重试的机制处理。重试失败后,交由配置服务器仲裁,配置服务器继续重试,依然失败就将数据服务器标记为临时故障。

  • 各个客户端定期从配置服务器获取最新的配置信息,此服务器临时故障信息就被所有的客户端知晓。



3.4 临时故障处理过程



  • 临时故障恢复过程中,只从正常服务器节点读取数据

  • 临时故障恢复过程中,向正常服务器节点和临时服务节点(临时的备份节点)写入数据,依然保证多写。备份节点写入的为操作日志,便于后续迁移。

  • 临时故障节点恢复后,应用服务器停止写入临时服务器,开始写入已恢复节点。临时服务器向已恢复节点迁移数据,如果出现写入冲突,需要根据时间戳判断裁定那个数据生效。

  • 如果临时故障节点恢复后,依然写入临时节点,那迁移动作很难确定完成,无法确定完成也就无法将写入操作切换回已恢复节点。切换过程有可能瞬间丢失数据造成节点间数据不一致

  • 如果临时故障节点恢复后,不在写入临时节点,只数据迁移,已恢复节点会丢失迁移过程中继续写入的数据,依然造成数据不一致。或者迁移过程中暂停所有节点的写入操作,迁移完成后再开放,数据一致可以保证但丢失可系统可用性。

  • 在故障节点恢复后,立刻暂停临时节点写入工作,让已恢复节点同时承接正常写入和数据迁移工作,保证系统一致性和可用性。

  • 迁移完成后,系统临时故障完全恢复。已恢复节点可以继续提供读取服务。(迁移完成前故障节点数据不全)

故障恢复过程读写状态



  • 故障中:写正常节点和临时节点,只读正常节点。

  • 临时故障节点恢复:写正常节点和已恢复节点,只读正常节点。

  • 迁移数据完成(系统完全恢复):写正常节点和已恢复节点,正常节点和已恢复节点任意读取。



4. 总结

本文通过路由算法和故障转移介绍了Doris如何实现自己中等规模和高可用的设计目标,下篇将介绍如何通过自动节点伸缩保证系统可扩展性。

发布于: 2020 年 07 月 15 日 阅读数: 51
用户头像

Jerry Tse

关注

还未添加个人签名 2018.11.02 加入

还未添加个人简介

评论

发布
暂无评论
聊聊Doris的架构设计-上篇