感谢阅读「美图数据技术团队」的第 16 篇原创文章,关注我们持续获取美图最新数据技术动态。
2008 年成立至今美图打造了数款 App 产品,而随之带来的是扑面而来的用户数据与其相关的业务需求。多种业务需要不同的统计方式,数据量的暴涨意味着维护成本也在同步提高,愈来愈多的重复性工作......这些都是美图进行数据「改革」的主要因素。
图 1
如图 1 所示是最初始的需求提出到完成的具体流程,其中服务端设计接口并产生日志,统计人员需要和服务端确认接口,开发统计上线,期间需要反复与服务端人员沟通,时间成本较大。各种业务场景都是由各自产品提需求给统计小组,复杂的业务分析使得沟通和执行成本较大,长期的效果跟踪下需求不断变更,产品端的数据分析人员对各自产品更加敏感,能更灵活的调查。
根据以上问题我们在各个问题爆发的阶段采取不同的策略对应:
通过一系列的改版迭代,在数据开发过程中减少沟通成本、加快开发周期、减少重复开发工作、不惧需求变更、节约维护成本、提高数据质量.....
/ 结构设计与实现原理 /
图 2
如图 2 所示是系统的架构设计,主要分为两个模块:Manager 与 Scheduler。其中 Manager 包括系统界面管理模块和任务管理模块,任务管理模块包括任务配置、调度配置、插件配置、用户信息配置等 Meta 信息。Scheduler 负责任务执行生命周期与任务实例插件初始化、数据抽取、转换、写入、日志记录等。
接下来看看任务执行的主要流程:
图 3
首先用户提交执行任务,Manager 验证参数并向 Scheduler 发起执行任务,返回提示信息给用户。接着 Scheduler 初始化任务实例 Workflow,加载配置信息,Workflow 初始化任务配置的插件(SourcePlugin、TargetPlugin 以及 Udf)。SourcePlugin 连接数据源,执行抽取数据,Udf 对抽取的数据进一步加工,进行聚合等。TargetPlugin 连接数目标数据源,把抽取的数据写入指定目标库(如执行 Mysql Insert 写入数据)。最后完成 Workflow 执行,记录完成信息。
图 4
值得注意的是,每个 Workflow 实例都会执行 SourcePlugin.extract、Udf、TargetPlugin.loading。
插件接口主要有两个方法:extract、loading,extract 定义为抽取数据,loading 定义为写入数据。每个具体的插件实现以上两个接口即可,之后有其他数据源可以方便扩展,通过以下图表了解整体插件:
图 5
/ 系统核心 feature /
上文中有提到在美图数据平台化之后,由于业务需求与数据分析需求的爆发我们搭建了业务配置版与数据分析版两种平台,其中在业务配置版本业务方只要按一定规范进行日志记录,即可在平台进行简单的任务配置并调用统计接口获取数据进行展示。业务方可以进行如下配置:
数据来源
对接数据之后,数据将会分类并映射为 hive 表,比如美拍有服务端接口日志 meipai 表;
分组维度
根据数据接入时候定义字段,如美拍日志中的 client_id,分组维度相当于 group by 所需字段。
统计维度
需要统计的方式和目标,支持计数、去重、求和、top,选择计算方式之后,需要再选择计算目标,比如对 device_id 计数、对金额求和,结合统计方式形成如 count(device_id),count(distinct device_id)的sql 形式。
过滤条件
对日志进行条件过滤设置,支持 or 和 and,不同组之间的条件关系为 or,组内的条件为 and,类似于 sql 中的 where (a and b ) or (c and d);支持各种条件过滤逻辑。
聚合维度
支持聚合功能,可自由选择聚合的维度和聚合后分组维度的代替值。
接口访问
需任务配置完成生成 api ,经过授权即可访问数据。
数据分析版本面向有一定 sql 敏感度的数据分析人员或者服务端人员,该版本提供一个分析平台并支持下载数据,它有以下功能:
快速校验错误语法
基础语法检验,即时反馈 sql 语法中的错误;
危险语法限制
drop、insert 基础表数据等;
⾼资源损耗验证
限制查询的时间范围,通过 partition 的日期字段判断每个子查询的时间范围的有效性。
先来看看 Hive 的简要架构图,在 Hive 的架构中,Compiler 完成 HQL 查询语句从语法解析、语义解析、编译、优化以及生成查询计划等工作。
图 6
其中 HQL 转化成 mapredurce 的过程,分为以下 6 个阶段:
图 7
*AST:抽象语法树;QB:查询基本单元QueryBlock;OP Tree:执行操作树;Task Tree:任务树。
1.HQL 词法、语法解析,将 HQL 转化为抽象语法树 AST
语法解析阶段,Hive 利用 Antlr 将用户提交的 HQL 语句解析成一棵抽象语法树。
*ANTLR(Another Tool for Language Recognition),一个通过语法描述来自动构造自定义语言的识别器(recognizer),编译器(parser)和解释器(translator)的框架。
图 8
这里以一个简单的 sql 来说明 AST 的各个节点,图中生成一个 TOK_INSERT 节点,这个节点是在语法改写中特意增加了的一个节点。原因是 Hive 中所有查询的数据均会保存在 HDFS 临时的文件中,无论是中间的子查询还是查询最终的结果,Insert 语句最终会将数据写入表所在的 HDFS 目录下。
2. 遍历 AST,抽象出查询的基本组成单元 QueryBlock
AST 仍然非常复杂,不足以结构化也不方便直接翻译为 MapReduce 程序,AST Tree 转化为 QueryBlock 就是将 SQL 进一部抽象和结构化。
QueryBlock 是 SQL 最基本的组成单元,它包括三个部分:输入源、计算过程、输出。简单讲一个 QueryBlock 就是一个子查询。
*QB#aliasToSubq(表示QB类的aliasToSubq属性)保存子查询的QB对象,aliasToSubq key值是子查询的别名;
QB#qbp即QBParseInfo保存一个基本SQL单元中的各个操作部分的AST Tree结构; QBParseInfo#nameToDest这个HashMap保存查询单元的输出,value是对应的ASTNode节点,即TOK_DESTINATION节点。类QBParseInfo其余HashMap属性分别保存输出和各个操作的ASTNode节点的对应关系;
QBParseInfo#JoinExpr保存TOK_JOIN节点; QB#QBJoinTree是对Join语法树的结构化; QB#qbm保存每个输入表的元信息,比如表在HDFS上的路径,保存表数据的文件格式等; QBExpr这个对象是为了表示Union操作。
3. 遍历 QueryBlock,翻译为执行操作树 OperatorTree
该步骤是把查询单元 QB 转换操作树。操作树由多个操作符组成,每个操作符在 Map 阶段或者 Reduce 阶段完成单一特定的操作。以下是基本的操作符:
图 9
QueryBlock 生成 Operator Tree 就是遍历上一个过程中生成的 QB 和 QBParseInfo 对象的保存语法的属性,各个属性对应生成操作符,包含如下几个步骤:
QB#aliasToSubq => 有子查询,递归调用 QB#aliasToTabs => TableScanOperator QBParseInfo#joinExpr => QBJoinTree => ReduceSinkOperator + JoinOperator QBParseInfo#destToWhereExpr => FilterOperator QBParseInfo#destToSelExpr => SelectOperator QBParseInfo#destToGroupby => ReduceSinkOperator + GroupByOperator 最终都解析完后,会生成一个 FileSinkOperator,(将数据写入 HDFS)
由于 Join/GroupBy/OrderBy 均需要在 Reduce 阶段完成,所以在生成相应操作的 Operator 之前都会先生成一个 ReduceSinkOperator,将字段组合并序列化为 Reduce Key/value, Partition Key。
4. 逻辑层优化器进行 OperatorTree 优化
优化器类型如图 10 所示:
图 10
5. 遍历 OperatorTree,翻译为 MapReduce 任务
以下是操作符生成对应 mr 任务的具体规则:
图 11
该过程分为 5 个阶段:对输出生成 FetchTask;从 OperatorTree 的其中一个根节点向下优先遍历;ReduceSinkOperator 标示 Map/Reduce 的界限,多个 Job 间的界限;遍历其他根节点,遇到 JoinOperator 合并 MapReduceTask;剪断 Map 与 Reduce 间的 Operator 的关系。
6. 物理层优化器进行 MapReduce 任务的变换,生成最终的执行计划
以下是各种物理层优化器的作用:
图 12
过程中值得注意的是:
对用户提交的 sql 进行校验与限制,主要复用了第一阶段生成的 AST 和第二阶段生成的 QB; 递归 QB 从 AST 的节点获取操作、表、查询条件等信息; 判断操作和表是否有危险行为与权限,主要为 drop、insert; 查询条件解析出分区字段,验证分区字段必填和时间范围。