Apache HAWQ的资源管理之资源管控 [作者:霍瑞龙]

资源是软件系统中重要的对象,通常所讲的资源包括:内存,CPU时间片,网络I/O和磁盘读写带宽等。


在Apache HAWQ中,我们利用资源管理器来完成对这些重要资源的管理。它包含两方面的功能:


1) 对外与资源拥有者(如操作系统,YARN等)通信完成资源的申请和释放;


2) 对内进行资源管理,并将其分配给使用者,同时管控使用者,防止被资源过度使用。


由于资源的稀缺性,并发的查询在HAWQ中执行时必然产生对资源的竞争。为了避免一些查询过多占用资源而使得其他查询无法被调度或者执行,我们采用了资源管控(Resource Enforcement)的方法,从而保证不同查询之间对资源使用的公平性,最终达到保证系统可用性,提高系统吞吐量和稳定性的目的。


鉴于内存和CPU的工作原理和特性不一样,我们采用不同的策略来分别处理。具体来讲:


1) 内存在使用时以字节(byte)为单位,通过申请(malloc)和释放(free)以固定的量来完成。一次申请只存在成功或者失败两种可能;


2) 而CPU时间片则是通过操作系统内核的任务调度器的动态调度给不同的执行进程,一个进程在执行阶段可能会多次被换入换出到CPU上,因此它是"相对比例“的方式。


下面就HAWQ中资源管控的设计和实现从内存和CPU两方面进行介绍:


1. 内存管控


查询计划(Query Plan)是一个树形的结构(如图1中TPC-H Query 12的查询计划),它是查询在执行阶段的逻辑和物理表示。该结构中的各个操作符(Operator)都需要或多或少的内存来完成执行。


图1: TPC-H Query 12的查询计划


理论上来讲,内存的管控可以做到这棵树的任何粒度上:小到查询处理的每个操作上,大到整个查询上。但实际上由于对期望内存用量的估计不准,许多操作符都可能超过预计的内存用量。换句话说,如果我们在较低级别的操作符上进行内存用量的管控,将会出现频繁的内存不足(Out of Memory),从而导致大量的查询被中止(Query Abort)。很显然,这会使 HAWQ变成不可用的系统。实验表明,查询级别也存在同样的问题。


观察并发的查询在HAWQ中执行的情况就可以发现:大部分进程在其生命期内使用内存的模式是“申请 - 执行 - 释放”的重复过程。鲜有进程会申请所有内存,执行完毕后才释放所占用内存。如果我们在物理节点的级别管控 所有并发执行的查询的内存用量,就可以达到“错峰”的效果。


如果把HAWQ看成一个应用,我们只需要保证HAWQ中所有查询用到的内存总量不会超过资源管理器从资源所有者申请给它的资源总量即可。这样的处理会带来两个好处:


1)绝大部分情况下所有并发的查询可以顺利执行;


2)HAWQ占用的内存不会超过其申请到的总量,保证集群中其他应用的可用性(如Hadoop的Map Reduce任务等)。



图2:内存管控器的原理


图2给出了HAWQ中内存管控器(Memory Enforcer)的原理以及与其他模块的交互过程(如资源管理器等)。而它的实现主要包括以下模块:


1) 实际内存用量监控器(Memory Monitoring Framework ):负责在每个从节点上统计不同粒度上的操作符的内存使用量,并汇总到高层次级别(如查询级别,会话级别,节点级别等)。


2) 期望内存用量估计与更新:负责从主节点上的资源管理器得到当前从节点上的内存可用量(Segment Memory Quota),该值将作为我们判断是否存在“过度”使用内存的标准。


3) 热点区域处理器(Red Zone Handler):负责检查当前从节点上所有的查询执行器所用的内存量是否超过期望内存用量的一定比例(如80%)。如超过,它就会启动异常查询处理器来清除内存用量较多的查询,避免当前从节点“过热”。


4) 异常查询处理器(Runaway Query Terminator):负责遍历当前从节点上执行的所有查询,找出内存用量最大者并将其撤销,从而使当前从节点进入合理的内存使用状态。


2. CPU管控


直观上来看,在不同查询间协调CPU时间片,从而让它们占用不同比例的CPU资源有一点无从下手。这里,我们借助Linux内核提供的Control Group(CGroup)。它可以将内存,CPU,网络,磁盘读写等资源子系统或者它们的任意组合分配给一个或一组正在执行的进程,并限制其用量保持在合理范围内。


在HAWQ中,一个查询的执行实际上由各个从节点上的一组查询执行器进程(Query Executor Process)完成。


基于此,我们通过查询代价计算出查询的CPU资源(以vcore为单位),然后将查询的vcore转换成CGroup中对应的cpu.shares的权重,最后借助CGroup调度属于该查询的CPU时间片,从而达到CPU管控的目的。


CPU管控的典型处理过程如图3所示:


图3:CPU Enforcer原理


详细来讲,它主要包含以下过程:


1) 主节点(Master)上的资源管理根据查询计划和查询代价得到查询需要的CPU资源(以vcore为单位)。


2) 查询计划分发器(Dispatcher)将查询计划和资源分发到参与该查询的从节点(Segment)上。


3) 从节点上的查询执行器接受到查询计划和CPU资源量后,向从节点上的资源管理发送MoveToCGroup的请求。


4) 从节点上的资源管理器将该请求交给它的一个专门负责操作CGroup的服务线程。


5) 从节点上的服务线程将该QE进程的PID加入到CPU子系统中对应的cgroup.procs,并将CPU资源量vcore转换后写入cpu.shares。


6) 在查询执行器执行阶段,CGroup将根据不同查询中的cpu.shares的权重的比例在内核中分配CPU时间片。


7) 查询执行器执行结束后,在进程退出前它会向从节点上的资源管理发送MoveOutCGroup的请求,这时服务线程会将对应的PID从cgroup.procs中移除。


并发执行的查询对应的CGroup的目录结构如图4所示:


图4:并发的查询对应的CGroup的目录结构


鉴于篇幅的关系,本文只简要介绍了Apache HAWQ中内存和CPU等重要资源管控的设计和实现。如果大家感兴趣,我们可以在后续的分享中详细介绍各个部分。


对于热衷开源,愿意一起推动国内开源大数据社区的朋友,可以贡献到https://github.com/apache/incubator-hawq。


更多精彩内容,请关注大数据社区公共帐号!

长按识别图片二维码

猜你喜欢

转载自blog.csdn.net/bigdatacommunity/article/details/49683845