本文缘起社区的一个提问,正好也想要了解其计算逻辑。
SQL联结查询中右表被广播的 “判断条件”,左表与右表的数据量比例差多少右表才被广播?
有关Join查询的注意事可以参考:Join查询的性能注意事项。
介绍
Impala 查询计划员会为执行 Join 查询而在不同技术之间选择,这取决于表的绝对和相对大小。Broadcast joins 是默认设置,其中右侧表被认为小于左侧表,而其内容将发送到查询中涉及的所有其他节点。替代方法被称为 partitioned join ,这更适合于大概同等大小的较大表的名称。使用此方法,每个表的各个部分将发送到相应的其他节点,在这些节点中可以并行处理行的子集。选择广播或分区 join 还取决于通过 COMPUTE STATS 语句收集的合并中所有表可用的统计信息。
查看哪种 join 策略用于特定查询,请为该查询发出一个 EXPLAIN 语句。如果发现查询使用广播 join,并且您知道通过基准测试知道分区 join 会更有效,反之亦然,那么向此查询添加提示以指定要使用的精确 join 机制。
影响因素:
统计信息
如果表或列的统计信息对合并中的一些表不可用,Impala 仍然会利用可用信息对表重新排序。带有统计信息的表位于合并订单的左侧,并根据整体大小和基数的成本按降序排列。不带有统计信息的表按零大小对待,也就是说将它们始终放在合并订单的右侧。
利用 STRAIGHT_JOIN 覆盖 Join 重新排序
如果 Impala 合并查询由于统计信息过期或意外数据分发而效率低下,您可以在使用 STRAIGHT_JOIN 关键字前先立即使用 SELECT 关键字从而让 Impala 避免对合并表重新排序。STRAIGHT_JOIN 关键词将关闭 Impala 内部执行的合并子句重新排序操作,并生成依赖于在查询文本中以最佳方式进行排序的合并子句的计划。
查询提示
/* +BROADCAST */
和 /* +SHUFFLE */
提示可控制合并查询的执行策略。在查询的 JOIN 关键词后面指定以下其中一种结构:
/* +SHUFFLE */
- 让合并操作使用"分区"技术,该技术可使用用散列算法分割这两个表格中对应的行,并将行的子集发送给其他行以进行处理。(关键字 用于表示"已分区合并",由于这种类型的合并与"已分区表"不相关。)由于当表和索引统计信息不可用时可替代"广播"合并机制为默认选项,您可能在广播合并不适用的情况下使用该查询提示;通常,已分区的合并对大小相似的较大表之间的合并更为有效。/* BROADCAST]
- 让使用"广播"技术的合并操作,该操作指将右侧表中的所有内容发送到处理合并时涉及的所有节点。当表和索引统计信息不可用时,这是默认操作模式,因此通常只有过时的元数据导致 Impala 错误地选择分区合并操作时您才用得到。通常情况下,当一个表格比另一个表格小得多的情况下,广播合并才更为高效。(将较小的表放在 运算符的右侧。)
查询选项
BROADCAST_BYTES_LIMIT 广播输入大小的限制
DEFAULT_JOIN_DISTRIBUTION_MODE 此选项确定当联接查询中涉及的任何表缺少统计信息时,Impala使用的联接分布。
如何确定 Join 策略
与主流的数据库和数仓查询引擎一样,Impala 也是基于代价模型进行执行计划优化(CBO)。只有获取足够的统计信息,才能支撑 Impala 选取较优的执行计划。
本节分析 Join 的计算方式,以解文章开始提出的问题。对于这两种 join 类型,总成本计算为通过网络发送的数据量,加上插入到哈希表中的数据量。
1. 计算两种 join 成本:
能计算的前提是有统计信息,否则都是 -1
.
broadcast:将右侧 Fragment 输出发送到左侧的每个节点,并在节点构建哈希表。
计算:(右侧Fragment 数据大小 + 哈希表大小)* 左侧实例数
// rhsDataSize 是咋计算的呢?
rhsDataSize = Math.round(rhsTree.getCardinality() * ExchangeNode.getAvgSerializedRowSize(rhsTree));
// rhsTree.getCardinality() :右侧 Fragment 的基数,计算方式是
// ExchangeNode.getAvgSerializedRowSize(rhsTree) :返回序列化以通过 exchange 发送的“ exchInput”产生的平均行大小,值为平均行大小的1.1倍
// 广播成本
broadcastCost = 2 * rhsDataSize * leftChildNodes;
repartition:左右侧 Fragment 都在联结 exprs 上进行了分区(EqJoinConjuncts 等值连接词),并使用右侧 Fragment 的输出构建了一个哈希表。
计算:左右侧 Fragment 网络成本 + 哈希表大小(使用的是右侧 Fragment 的输出)
// 网络发送成本
// 如果只有数据分区参与联结,lhsHasCompatPartition 是 true,说明不需要进行网络传输。
// lhsHasCompatPartition = analyzer.setsHaveValueTransfer(lhsDataPartitionExprs, lhsJoinExprs, false);
// 左侧网路成本
double lhsNetworkCost = (lhsHasCompatPartition) ? 0.0 : Math.round(lhsTree.getCardinality() * ExchangeNode.getAvgSerializedRowSize(lhsTree));
// 右侧网络成本
double rhsNetworkCost = (rhsHasCompatPartition) ? 0.0 : rhsDataSize;
// 分区成本
partitionCost = Math.round(lhsNetworkCost + rhsNetworkCost + rhsDataSize);
广播成本 = 2 * 右侧数据大小 * 左侧实例数;
分区成本 = 左侧数据大小 + 右侧数据大小 + 右侧数据大小
换算之后:左侧数据大小 vs 左侧实例数 * 右侧数据大小 ≈ 左侧单个实例数据量 vs 右侧数据大小 ≈ 左侧平均文件大小 vs 右侧数据大小
todo: Impala 如何确定执行任务的实例数
2. 计算分布模式,决定使用哪种分布模式
注:重点是第 4 点,这块是根据统计数据算出来的分布模式,由此可以回答文章开头提出的问题。<