前言
TDW 是腾讯内部最大的离线处理平台,也是国内最大的 HADOOP 集群之一。在运营这么大集群的时候,运营面临各种各样的难题,在解决这些难题的过程中,团队提炼出来的一个运营理念,用两句话去描述。
- 用建模的思路去解决运营的难题
运营的问题怎么解决?你必须用一些数据建模的办法,把这个难题解析清楚,然后我们再去考虑运营平台建设。 - 运营平台支撑模型运作
不是为了建设运营平台而建设,而是它必须有一定的运营理念。下文写到这样的运营理念是怎么贯穿在迁移平台的建设里面的。
本文主题主要包含以下几个方面:
1、介绍一下腾讯大规模集群 TDW,以及为什么做迁移。
2、迁移模型是怎么样的。
3、迁移平台是怎么做的。
腾讯大规模集群TDW
先介绍一下腾讯大规模集群,我们这里讲的集群是指 TDW。TDW 是腾讯分布式数据仓库,它是一个海量数据存储和计算平台。为什么说是大规模集群?记得刚开始接手 TDW 运营的时候,很多年前,当时我们有400台的集群,觉得我们集群已经很大了,但是过了几年之后,我们要运营的集群已经达到4400台。现在看来4400台还算挺大的,但是又过了几年后,我们规模到了8800台,这是我们的现状。我们的现状可以用三个指标来描述:
1、单集群 8800 台
2、支撑每天 20PB 扫描量
3、同时提供 200PB 存储能力
所以我们说是“大”,用苹果一句话说,biger than biger,我们预计到2017年底我们规模会达到2万台。
运营这么大规模的集群,运营人员自然会面临很多挑战。比如像设备运维、版本上线变更、配置管理、还有快速扩容等等。这些问题,我们都有相应系统去支撑,本文说的是我们遇到的另外一个头痛的问题:集群不断膨胀,从400台到8800台,前期可以通过扩容解决。到目前这个阶段,8800台之后,我们发现扩容已经搞不定了。为什么?因为现有机房的容量和网络架构只能支撑这么大的规模,这时候我们需要将 TDW 迁移到其他城市更大容量的机房,这也就是我们面临的另一个问题,跨城迁移。
上文说了 TDW 的迁移原因,现在回过头来看 TDW 的整体架构。TDW 是腾讯大数据的处理平台的一部分,整个腾讯大数据处理平台包含了下面五层。
我们从下往上看:
1、最底层是数据存储层,包括 HDFS、HBase、Ceph、PGXZ;
2、第二层是资源调度层;
3、第三层是计算引擎层,包括 MR、Spark 和 GPU;
4、第四层是计算框架,包括 Hermes、Hive、Pig、SparkSQL、GraphX 等;
5、最上层是服务层,提供给外部数据分析能力和机器学习能力。
这是整个腾讯大数据平台,刚才说的 8800 覆盖了其中离线数据处理的部分。我们整个迁移覆盖了 HDFS、盖娅、MR、SPARK、HIVE、Pig 和 SparkSQL。
迁移模型是怎么样的
跨城数据迁移到底难在哪里?
首先,运维工作量非常大。有上百P的数据要腾挪,有几十万任务需要切换,还有近万台的设备需要搬迁,这个事情对于运维来说工作量非常大。
其次,要保障业务无感知。迁移过程中系统要稳定可用,要保障数据不能丢失,不能把一份数据从一个地方搬迁到另外一个地方的时候,把数据弄丢了。
最后,要保障任务的计算结果准确而且任务的运行时长不能有明显的波动。
不管是运维工作量大还是业务无感知,这都还不是最致命的,对于跨城迁移来说,最致命的问题是:当数据和计算分散在两个城市的时候,数据穿越可能造成专线阻塞,从而影响使用专线的所有系统,导致影响扩大化。在介绍跨城迁移模型之前,我们先简单介绍两个方案,一个是双集群方案,一个是单集群方案。
方案一:双集群方案
双集群方案比较好理解,左侧跟右侧是两个城市的集群,双集群方案就是两套完全独立的系统,让它们独立去跑。
在说方案之前,我再深入介绍一下 TDW 里面的几个模块。我们只看左边就可以了。左边从下面最底层是 GAIA,GAIA 负责资源调度。中间最左侧是数据采集 TDBank,它负责把各个业务线数据收集到 TDW。TDW 的核心是计算引擎和存储引擎,存储引擎是放数据的地方,计算引擎提供 MapReduce 和 Spark 的计算能力。之上有查询引擎,最上面提供两个用户入口,任务统一调度和集成开发环境 IDE。
举两个例子来说说各模块是怎么交互的。
案例一,数据是怎么进入 TDW 的?
首先业务数据经过数据采集模块,落地到存储引擎的某个目录下;统一任务调度 Lhotse 配置的一个入库任务,与 Hive 交互,将目录的数据转换成 Hive 表的数据。
案例二,数据是怎么计算的?
数据计算通过任务触发,任务是对数据的处理加工,比如统计日报的时候,计算任务对某个表做操作,把结果写回到另一个表中。迁移是把存储和计算整套 TDW 平台,从一个城市搬迁到另外一个城市,双集群方案思路就很简单,在另外一个城市把所有系统都搭起来,跑起来就好了。系统在两个城市之间是完全独立的,比如数据两份,计算两份,在这两个独立的系统之间不需要有任何的数据穿越(除了在迁移本身的数据穿越)。这个方案最大优点就是不需要数据穿越,业务可以做到完全无影响,但是它最大缺点是需要大量的冗余设备。
方案二:单集群方案
下面讲一下单集群方案,它跟双集群差异点在哪里?最核心的差别在于:存储不会同时在两个地方,要么在左边,要么在右边。
单集群方案有一个最大的优点就是不需要大量的设备,慢慢地把一部分设备,一部分业务,从左边迁移到右边。这里会面临一个问题,比如刚才说到的一个计算的场景,如果没有控制好的话,会出现计算在左侧,数据已经跑到右侧去了,因为数据只有一份。任务跑起来的时候,左侧的计算引擎就会大量拉取右侧的数据,会对专线造成很大的风险。
对比一下刚才那两个方案,我们可以总结一下思路:在一个大的系统里,如果优先考虑成本,建议采用单集群方案。单集群方案最大风险是跨城流量控制,跨城流量控制最重要的点是:数据在哪里,计算就去哪里,要不然就是穿越;如果访问的数据两边都有,哪边数据量大,计算就在哪边。
建立基于关系链的迁移模型
前面我们分析了一下我们实现跨城迁移的问题和方案,接下来我们为了解决跨城的流量控制降低跨城迁移的流量,我们引入一个基于关系链的迁移模型。
我们需要知道数据流是怎么样来的,比如上面的一个关系链中,入库任务对最顶层的 HDFS 数据做一些加工处理,处理之后把结果保存到入库表;分析人员基于这个入库表做各种计算和统计分析,比如统计某些指标,做关联性分析,这里配置了四个任务,这四个任务运行后产生新的结果表,其中还有两个结果表由下层的任务做进一步的处理,这样就产生了数据和任务的关系链。引入关系链模型,它能帮助我们理清楚数据和任务的关系。我们用椭圆描述数据,矩形描述对数据的加工,他们的连线表明访问数据的方向,是读还是写。这个关系可以用来指导我们的数据迁移,可以做到数据在哪里,计算就在哪里。
关系链的生成
接着的问题是在一个大的系统里关系链怎么生成?在任务调度里面有一个概念,叫做依赖,用来描述任务的父子关系,父任务运行完成后子任务才允许运行。原来我们没有做关系链的时候,这是纯粹的任务调度层的关系,虽然它有一定指导作用,但是不能直接应用在迁移里面,因为我们需要的是数据和任务的关系,而不仅是任务和任务的关系,我们需要从庞大的任务管理系统生成关系链,来指导数据迁移。
接下来介绍一个叫 hadoopdoctor 的运营工具,它是用做什么的?它会把我们跑的任务信息采集回来,把它保存在 DB 里面,这些信息用于定位 MR 失败原因或性能分析。它有一个主控模块,每五分钟去所有的 NodeManager 采集每个 MR 的配置和运行信息,比如说它的访问数据是什么,输出结果是什么。为了支持迁移,我们改了一些逻辑,让 hadoopdocter 记录数据路径和任务ID,同时区分标识是读的还是写的。把这个数据采集出来以后,我们就可以做关系链的分析。
这里面采集到的路径会非常多,比如一个日报可能访问的是昨天某一个表的数据,比如访问量,就需要访问昨天的分区。采集出来的数据路径粒度非常细,它是包含日期的。但是我们关注点并不需要到分区,我们关注的是表本身。所以我们把涉及到日期相关的路径规约掉,转成与日期无关的路径,数据规约对关系链分析有好处,归完之后减少了很多的数据量。我们把最基础的信息采集到,它描述了一个任务,访问什么数据,产生什么数据。经过我们的逻辑采集完之后,我们得到的是最原子的数据访问关系,就是一个任务对存储的操作,读或者是写,我们会产生非常多这样的原子关系,这种关系累计的结果就是关系链。我们清洗出来一个最基础的关系,可以拼凑成一个大的关系链。
切分大关系链
关系链里面特别注意的地方,是一定要覆盖全面。统计分析里不仅有日报,还有周报和月报,统计周期一定要覆盖较长的时间范围,这样才能把所有关系描述准确。从最基础的原子关系聚合成关系链,可以用并查集算法。聚合出来的关系链,有大有小。对于小的关系链,很简单就可以把它迁走;但是我们发现一个很头痛的问题,聚合除了产生很多小的关系链,同时也产生了少量非常大的关系链,一个大的关系链可能包括超过十万个结点。这时候我们发现回到原点,本来想把整个数据仓库从一个城市挪到另外一个城市,思路是将它打散生成多个关系链,最后也确实产生一些小的关系链,方便我们做迁移。但是遗留了一些大的关系链。
如果看这个图我们自然而然想:既然这么大的关系链迁不动,就先迁上面部分,这里就是把大的关系链切开的想法。我们引入一些关键结点,这些关键点把大的关系链切分成多个小关系链。那么哪个关系链结点才是最合适的?这个也很难找,大家可以想理论上是存在的,比如从图中标红色部分一刀切过去就可以一分为二,但是在上十万结点规模的关系链里是很难做到这个事情的。
引入HIVE双写表
把这个问题先留在后面,我们先做一个假设,已经找到合适的结点了,怎么实施关键结点的迁移,有两种思路:一种是单份数据方案,比如把这份数据迁到另外一个城市,就让它穿越,很容易实现,但是不好控制流量;另一种是双份数据方案,把关系链一分为二的时候,就把关键结点的数据变成两份。我们引入双写表的概念,双写表有两个 location。
双写表的数据有两份,在城市A访问数据的时候,城市A 的 HIVE 会返到主的 location,城市B则返回备的 location,计算可以访问离自己近的存储。这里,需要一个任务把城市A的数据同步到城市B中,这是一个同步任务,这个任务在数据在城市A生成后(总有个计算任务往里面写数据),把城市A的数据同步到城市B,保证两边的数据是一致的。在迁移过程中把一个表升级成双写表的过程业务是无感知的,我们有机制保证数据双份,就近访问。
数据一致性保证
刚才说到双写表在城市A和城市B有两份数据,由同步任务负责同步,这时候我们会遇到一个问题,在城市A和城市B,它的下游任务会不会在同步任务没跑完之前就去访问这个数据?
我们必须要保证数据的一致性,这通过增加对同步任务的依赖实现。比如任务产生数据表1,下面三个任务会读取表1数据,这个依赖关系是上面这个任务是父任务,只有这个父任务跑完之后,下面三个子任务才能跑起来。这个依赖逻辑保证了三个子任务总能正确访问表-1的数据。
加入了同步任务之后,我们就保证了数据的一致性。比如这个图里,我们有两个任务从城市A迁到城市B,这时候我们要保持数据和计算的一致性,只需要保证城市B访问表-1数据的任务依赖同步任务即可。
最小化切分和关系链融合
回到一个大的关系链怎么拆分的问题,假设已经把它拆开了。拆开的时候产生了很多小的关系链,把小的关系链从一个城市迁移到另外一个城市的时候,为了减少数据穿量引入双写表的概念,双写表加上任务依赖,保证了所有拆分出来的关系链有一个比较非常好的特性,就是不管产生多少个关系链,每个关系链都是随时可以迁移的。还有一个问题:我们很难找到合适的关键点,对一个十万节点关系链,我们做了一些尝试,用遍历的方式查找所有可行的双写表,都不能把这么大的关系链拆开,我们发现不存在单个双写表可以拆开这么复杂的关系链。
大家可以想象一下,一个很复杂的图里面可能找不到一个结点能够将它切分成多个关系链。单个双写表做不到,则需要使用多个双写表,这时候找出合适双写表的算法就会非常复杂。后来我们做了一个变通,让所有符合双写表规则的表都变成双写表,这样可以实现对一个大关系链的最小化拆分。之后我们对最小化拆分后的小关系链又做了融合,把很小的关系链并成规模适中的关系链。
这里面整体的思路,类似于工厂流水线打包,我们把关系链聚合,再把它拆分融合,变成一个个大小适中的关系链,就像工厂打包的一个个箱子,迁移就是把这些箱从从城市A运输到城市B的过程。
计算的迁移
我再补充一下计算的迁移,刚才上面架构里面,说计算需要从城市A迁移城市B,怎么切呢?我们对每个任务加上城市 ID 的扩展参数,当这个任务需要从城市A切到城市B跑的时候,只需要改这个参数就好了。平台会记录城市 ID,并在不同系统上传递,进行准确的路由,这样实现任务是方便迁移的。
再总结一下迁移模型,我们面临运营上很头疼的事情,要做跨城迁移,我们解决的方案是基于关系链,把关系链打散,通过机制保证关系链的一致性,使得我们迁移能够跑起来。
注:本篇内容来自”腾讯技术工程官方号“,公众号ID:tegwzx