作者: 康凯森
日期: 2017-10-22
分类: OLAP
在Apache Kylin Cube 构建原理 一文中我们介绍了Kylin 的Cube构建原理,我们知道目前Kylin的Cube最终是要存储在HBase中,所以Cube构建的最后一步需要生成HFile,关于生成HFile的原理,注意事项,优化技巧我在Hive 数据 bulkload 导入 HBase中有介绍,本文主要介绍我是如何对Kylin中的HFile生成进行进一步优化,从而避免CubeHFileJob的Reducer阶段OOM。
我们知道,目前Kylin中生成HFile的reducer十分简单,就是用的HBase中的KeyValueSortReducer。 其代码很短, 逻辑也十分简单,就是利用TreeSet排序后,再输出。 但是这样会有一个问题,就是当1个rowkey对应的KeyValue很大且很多时,经常会OOM,而这种情况在Kylin Merge的时候十分容易发生。
TreeSet<KeyValue> map = new TreeSet<KeyValue>(KeyValue.COMPARATOR);
for (KeyValue kv: kvs) {
try {
map.add(kv.clone());
} catch (CloneNotSupportedException e) {
throw new java.io.IOException(e);
}
}
context.setStatus("Read " + map.getClass());
int index = 0;
for (KeyValue kv: map) {
context.write(row, kv);
if (++index % 100 == 0) context.setStatus("Wrote " + index);
}
前几天和我们HBase同学无意中聊起时,他提到可以将排序放到Shuffle阶段,这样Reducer节点就不用排序,自然不会有OOM问题,并且他很早已经向HBase社区提交了Patch,已经合入了HBase master分支 HBASE-13897。于是我就准备优化下Kylin的CubeHFileJob。
HBase中的实现不能直接在Kylin中使用:
综上,实际我们只需要实现一个RowKeyWritable,然后将PartitionFile和CubeHFileJob Mapper的Key都改为RowKeyWritable就可以。
虽然我们的灵感来源于HBase,但最终没有使用HBase现有的类,我们的实现更简洁,更优秀。
在实现过程中有一个坑需要注意,就是TotalOrderPartitioner在setconf的时候用的是ComparatorClass的确是mapreduce.job.output.key.comparator.class,但是其调用的方法是comparator.compare(splitPoints[i], splitPoints[i+1]),并不是compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2)。也就是说我们仅仅继承WritableComparator实现一个Comparator是不够的,我们还必须实现WritableComparable接口来重写compareTo(T o)方法,因为WritableComparator的compare方法调用了compareTo方法。