作者:
辛现银,花名辛庸,阿里巴巴计算平台事业部 EMR 技术专家。Apache Hadoop,Apache Spark contributor。对 Hadoop、Spark、Hive、Druid 等大数据组件有深入研究。目前从事大数据云化相关工作,专注于计算引擎、存储结构、数据库事务等内容。
HIVE TopN Shuffle
TopN 问题是排序中的一个经典问题。对于一个长度为 m 的数组,取其最大的 n (n <= m) 条数据,可以不必对整个数组进行全排。一般的算法对 m 进行全排的复杂度大约为 mlog2(m)。假设我们只取其中最大的 n 条,那么可以把这个复杂度降低到 m * log2(n)。如果 n << m,那么收益还是很大的。
HIVE-3562 引入了一个针对 TopN 的优化,即将带有 limit 算子的 order by 推至 map 端,这样 map 不必将所有数据 shuffle 到 reduce。order by 和 limit 算子在日常使用场景中经常一起出现,因此这个优化就显得很有必要。
抛开 limit 是如何下推的不管,我们这里只关注 ReduceSinkOperator 拿到 limit 算子后如何处理这个逻辑。代码入口是 ReduceSinkOperator#process(...) 方法。
ReduceSinkOperator 内维护了一个 TopNHash 变量 reducerHash,该变量决定了一条 row 是否被 collect,也就是说,是否被 shuffle 到下游:
// Try to store the first key.
// if TopNHashes aren't active, always forward
// if TopNHashes are active, proceed if not already excluded (i.e order by limit)
final int firstIndex =
(reducerHash != null) ? reducerHash.tryStoreKey(firstKey, partKeyNull) : TopNHash.FORWARD;
if (firstIndex == TopNHash.EXCLUDE)
{
return; // Nothing to do.
}
// Compute value and hashcode - we'd either store or forward them.
BytesWritable value = makeValueWritable(row);
if (firstIndex == TopNHash.FORWARD) {
collect(firstKey, value);
} else {
// invariant: reducerHash != null
assert firstIndex >= 0;
reducerHash.storeValue(firstIndex, firstKey.hashCode(), value, false);
其中有一个非常重要的方法TopNHash.tryStoreKey(...)
。它的意思是尝试把该行存到一个 topN 的 heap 中。之所以是尝试,是因为它有几个返回值