前言
聚合操作(Aggregation)是OLAP分析查询中最常见的操作之一,可以说它是数据分析查询的基石,它对应这SQL中的GROUP BY子句,OLAP中的上卷下钻操作无非就是对于GROUP BY和WHERE条件的改变,如何能够高效的实现聚合是决定OLAP分析性能的最重要因素之一(另外几个因素包括如何减少SCAN记录数、如何高效实现JOIN等)。
从OLAP引擎的划分来看主要分成MOLAP和ROLAP两大类,MOLAP基于CUBE模型进行预计算,可以直接将Aggregation之后的结果预计算并保存,这样查询的时候只需要查询已经预计算的结果或者做少量的运算就可以了;而ROLAP则始终基于原始数据计算,计算开销相对于MOLAP更大,但是它的优势在于灵活,不受限于模型的约束。前者的代表是Apache Kylin、Druid等;后者的代表则为Impala、Presto、SparkSQL等。
在分布式环境下,要做到高效的Aggregation就需要想办法增加并行度,Hadoop的出现解决了这个问题,想想经常提到的Word Count其实就是一个计算count(1)的Aggregation运算,因此本文就以一个简单的表和数据谈谈对分布式Aggregation的理解。
单机实现
说了这么多Aggregation,那么什么是Aggregation呢?虽然很简单但是在对校招生面试的时候发现很多同学对SQL的了解还停留在提到SQL立马反应就是“SELECT * FROM xxx”。聚合简单来说就是将某一列的值或者多列的组合值按照相同的值进行分组,每一个组内做聚合运算(对应着聚合函数),典型的聚合函数包括COUNT/SUM/MIN/MAX/COUNT DISTINCT等等,单线程情况下的聚合操作可以用如下的伪代码实现:
伪代码:对于某一列A进行COUNT(1)计算。
1: 构造一个Map<String, Int> M
2: 对于输入的每一条记录:
3: 获取A列的值a;
4: if(M.contains(a)) {
5: M.put(a, M.get(a) + 1);
6: } else {
7: M.put(a, 1);
8: }
9: 依次输出M的每一个<key, value>
这种实现在大数据场景下会存在两个问题:
- M这个缓存会占用很大的空间;
- 单线程执行,执行速度不能够让人满意。
分布式Aggregation
在分布式环境下,Hadoop的出现解决了这两个问题,想想入门Hadoop经常提到的“Word Count”程序其实就是一个根据单词计算COUNT(1)的聚合运算,那么Hadoop是如何利用分布式计算来解决这个问题的呢,它会把输入的记录分散到多个并行的运算节点中执行,每一个节点如上伪代码中的实现逻辑,记录的分散需要保证一条记录只会被一个计算节点处理(防止一条记录被多次处理),每一个计算节点会输出一个M,然后在经过汇总节点把所有的M进行汇总。
在介绍这个之前首先看一下分布式聚合运算的执行流程,如果要实现一个聚合函数(UDAF)