Hive - 两个表的高效连接

15

我正在使用Hive连接两个大型表(其中一个超过10亿行,另一个约1亿行):

create table joinedTable as select t1.id, ... from t1 join t2 ON (t1.id = t2.id);

我已经将这两个表以相同的方式进行分桶,通过每个id分成100个桶来进行聚类,但查询仍然需要很长时间。

有什么建议可以加快速度吗?

3个回答

18

当您将数据按连接键分组时,可以使用Bucket Map Join。为此,一个表中的存储桶数量必须是另一个表中存储桶数量的倍数。在查询之前执行set hive.optimize.bucketmapjoin=true;以激活它。如果这些表不符合条件,Hive将执行普通的内部连接。

如果两个表具有相同数量的存储桶并且数据按存储桶键排序,则Hive可以执行更快的Sort-Merge Join。要激活它,必须执行以下命令:

set hive.input.format=org.apache.hadoop.hive.ql.io.BucketizedHiveInputFormat;
set hive.optimize.bucketmapjoin=true;
set hive.optimize.bucketmapjoin.sortedmerge=true;

你可以在https://cwiki.apache.org/confluence/download/attachments/27362054/Hive+Summit+2011-join.pdf找到不同连接技术的一些可视化展示。


我来试一下。在Hive中,索引有帮助吗?例如,如果我通过id对t1和t2进行了索引? - maia
索引在连接操作中无法提高性能。它们用于检索单个行,例如在 WHERE id=123 中。桶是这里最好的选择。 - Adrian Lange
您可以在创建表时附加以下内容来指定存储桶的数量:CLUSTERED BY (id) INTO 192 BUCKETS - Adrian Lange
抱歉,我之前理解有误。列值是根据桶的数量进行哈希处理的。对ID列进行分桶,因为它应该具有不同的值,这样可以确保均匀性。 通过使用Bucket Map Join,Hive在桶上执行常见的Map-side Join。因此,桶的数量取决于表的大小和hive.mapjoin.smalltable.filesize的值,该值指定了Map-side Join中桶的最大大小(以字节为单位)。我建议使用更多的桶,因为无法保证桶的平均填充。 - Adrian Lange

16

在我看来,答案比@Adrian Lange提供的要复杂一些。

首先,必须了解BucketJoin和Sort-Merge Bucket Join (SMBJ)之间非常重要的区别:

要执行bucketjoin,“一个表中的存储桶数量必须是另一个表中存储桶数量的倍数”,如前所述,并且还必须将hive.optimize.bucketmapjoin设置为true。
发出连接命令后,Hive将在满足上述条件时将其转换为bucketjoin但请注意,Hive不会强制执行分桶!这意味着仅创建具有桶属性的表并不足以将表实际分成指定数量的存储桶,除非将hive.enforce.bucketing设置为true(这意味着在查询的最终阶段插入数据时减少器的数量实际上被设置为存储桶的数量)。
就性能而言,请注意使用bucketjoin时,单个任务将“较小”的表读入分布式缓存,然后映射器访问该表并执行连接 - 当表具有约100m行时,此阶段可能会非常漫长和低效!
之后,将像在规则连接中一样在减少器中完成连接。

要执行SMBJ,两个表必须具有完全相同数量的桶,以及在这些列上进行排序,并且还要将hive.optimize.bucketmapjoin.sortedmerge设置为true。
与前面的优化一样,Hive不强制执行分桶和排序,而是假设您确保表实际上被分桶和排序(不仅是通过定义,而是通过设置hive.enforce.sorting或手动对数据进行排序) - 这非常重要,因为它可能会导致两种情况下的错误结果
从性能方面来看,这种优化效率更高,原因如下:

  1. 每个mapper都读取两个桶,没有单个任务竞争分布式缓存加载
  2. 正在执行的连接是合并排序连接,因为数据已经排序,所以效率更高。

请注意以下考虑点:

  • 在这两种情况下,都应该执行set hive.input.format = org.apache.hadoop.hive.ql.io.BucketizedHiveInputFormat;
  • 在查询中应用/*+ MAPJOIN(b) */ (出现在select之后,其中b是较小的表)
  • 有多少个桶?
    应该从这个角度来看待:要严格应用于大表,因为它对此方面的影响更大,然后将该配置作为必须应用于较小的表。我认为作为经验法则,在一个桶中应该包含1到3个块,可能接近2个块。所以如果你的块大小是256MB,那么在大表中每个桶中有~512MB的数据似乎是合理的,因此这成为一个简单的除法问题。

还要注意,仅凭这些优化并不总能保证更快的查询时间。
假设您选择进行SMBJ,这会增加在运行连接之前对2个表进行排序的成本-因此您运行查询的次数越多,您为这个排序阶段所“付出”的代价就越少。

有时,简单的连接将带来最佳性能,上述任何优化都不会有所帮助,您必须通过调整MapReduce/Hive设置(如内存使用/并行性等)或在应用程序/逻辑级别优化常规连接过程。


0

我认为“一个表中的桶数必须是另一个表中桶数的倍数”不是 Map Bucket Join 的必要条件,我们也可以使用相同数量的桶。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接