根据Hive的版本和您的配置,您的问题的答案可能会有所不同。如果您可以共享确切的查询以及两个表的创建语句和其大小的估计值,那将更容易。
为了更好地理解问题,让我们来看一下Hive中“常规”内连接的工作原理。
Hive中的连接(MapReduce):
以下是Hive中如何将内连接编译为MapReduce的简化描述。通常,如果您有两个名为t1和t2的表,并使用连接查询,例如:
SELECT
t1.key, t1.value, t2.value
FROM
t1
JOIN
t2 (ON t1.key = t2.key);
其中,t1具有以下内容:
k_1 v1_1
k_2 v1_2
k_3 v1_3
其中,t2具有以下内容:
k_2 v2_2
k_3 v2_3
k_4 v2_4
我们期望连接结果为:
k_2 v1_2 v2_2
k_3 v1_3 v2_3
假设表格存储在HDFS上,它们的内容将被拆分成文件片段。映射器将以文件片段作为输入,并将键作为表的键列发出,值作为表的值列和标志(表示记录来自哪个表,即t1或t2)的组合。
对于t1:
k_1, <v1_1, t1>
k_2, <v1_2, t1>
k_3, <v1_3, t1>
的内容:
k_2, <v2_2, t2>
k_3, <v2_3, t2>
k_4, <v2_4, t2>
现在,这些发出的记录经过洗牌阶段,在此阶段,具有相同键的所有记录将被分组并发送到减速器。每个reduce操作的上下文是一个键和一个包含与该键对应的所有值的列表。在实践中,一个减速器将执行多个reduce操作。
在上面的例子中,我们将得到以下分组:
k_1, <<v1_1, t1>>
k_2, <<v1_2, t1>, <v2_2, t2>>
k_3, <<v1_3, t1>, <v2_3, t2>>
k_4, <<v2_4, t2>>
在reducer中会对值列表中的每个值执行乘法,如果这些值对应不同的表,则会执行乘法。
对于k_1,来自t2的没有值,因此不会发出任何内容。
对于k_2,发出值的乘积-k_2,v1_2,v2_2(因为每个表都有一个值,1x1 = 1)。
对于k_3,发出值的乘积-k_3,v1_3,v2_3(因为每个表都有一个值,1x1 = 1)。
对于k_4,来自t1的没有值,因此不会发出任何内容。
因此,您将获得您从内部连接中期望的结果。
好的,那我该怎么做?
你的数据可能存在偏斜。换句话说,当reducer接收到数据时,与某个键相关的值列表非常长,导致错误。为了缓解这个问题,你可以尝试增加JVM可用内存。在hive-site.xml中将mapred.child.java.opts
设置为像-Xmx512M
这样的值即可。你可以通过在Hive shell中执行set mapred.child.java.opts;
查询此参数的当前值。
你可以尝试使用“常规”连接的替代方法,例如map join。上述连接的解释适用于常规连接,在其中连接发生在reducers中。根据你使用的Hive版本,Hive可能能够自动将常规连接转换为更快的map join(因为连接发生在map阶段)。要启用优化,请将hive.auto.convert.join
设置为true
。该属性是在Hive 0.7中引入的。
除了将hive.auto.convert.join
设置为true
之外,你还可以将hive.optimize.skewjoin
设置为true
。这将解决1中描述的数据偏斜问题。