hive1.1.0查询和旧版本不一致问题分析

背景

在查询以下语句的时候结果不正确,无法join tab1.tm=tab2.hour。如果存在以下类似的语句,有子查询的group by,外层有join语句,返回的结果不正确,无法join成功。

SELECT tab1.ip, tab1.tm, tab2.HOUR
FROM (
    SELECT c.ip,c.lkid, c.HOUR,MIN(s.HOUR) AS tm FROM Tclick_iphone c JOIN Tsndata_iphone s 
    ON c.ip=s.ip GROUP BY c.ip,c.lkid,c.HOUR
) tab1 JOIN Tsndata_iphone tab2
ON tab1.ip=tab2.ip AND tab1.tm=tab2.HOUR;

分析

首先是构造可重复测试语句,减少所需要查询的数据,然后尝试替换其中的语句,检查语句是否正常。缩小错误范围。

测试中发现如果把子查询替换为表,能够正常查询。另外在随机测试的过程中,发现这样的语句也可以成功。

SELECT tab1.ip, tab1.tm, tab2.HOUR
FROM (
    SELECT c.ip,c.lkid, MIN(s.HOUR) AS tm FROM Tclick_iphone c JOIN Tsndata_iphone s 
    ON c.ip=s.ip GROUP BY c.ip,c.lkid,c.HOUR
) tab1 JOIN Tsndata_iphone tab2
ON tab1.ip=tab2.ip AND tab1.tm=tab2.HOUR;

单步调试1

反复单步调试对比错误、正确语句,发现第二步的输入和正确的语句不一样。

这是字节数组,还有个解析这个字节数组的描述信息(多少个字段,各个字段的类型) ,正确语句输入的内容为:

[3, 14, 49, 50, 49, 46, 50, 51, 54, 46, 49, 54, 52, 46, 50, 56, -114, 11, -82, 0, 0, 0, 0, 0, 0, 0, 0, 0]

描述信息有两个字段

错误的语句输入内容为:

[15, 14, 49, 50, 49, 46, 50, 51, 54, 46, 49, 54, 52, 46, 50, 56, 4, 119, 105, 102, 105, -114, 10, -84, -114, 11, -82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

描述信息有两个字段

这个字节第一位是判断符号,判断是否是null,后面的字符如果是string,第一位是字符长度。如果是数字,会有类似这样的bytes [-114, 10, -84]

从这里看出输入的内容不一致,但字段描述信息却一样。那错误可能发生在输入的内容不正确或者字段描述解析不正确。

单步调试2 – 输入内容、字段描述从哪里来

explain知道第二步的输入来源于子查询的输出,也就是子查询的reduce输出。单步调试发现字段描述是在编译语句阶段已经确定好,每个Operator已经确定了输出的字段数和类型。

另外在对比explain中,发现正确的语句比错误的语句多了个SELECT Operator(列裁剪)。

单步调试3 – 为什么错误语句少了SELECT Operator

范围缩小到语句的语法编译。

发现代码中会打印一些日志出来,可以修改hivelog4j文件改为debug模式,发现语句优化的过程中把SELECT Operator去掉了。

优化前

TS[0]-FIL[2]-RS[3]-JOIN[6]-SEL[7]-GBY[8]-RS[9]-GBY[10]-SEL[11]-FIL[13]-RS[14]-JOIN[17]-SEL[18]-FS[19]
ppd.PredicatePushDown: After PPD
TS[0]-FIL[21]-RS[3]-JOIN[6]-SEL[7]-GBY[8]-RS[9]-GBY[10]-FIL[20]-SEL[11]-RS[14]-JOIN[17]-SEL[18]-FS[19]

优化后

TS[0]-FIL[21]-RS[3]-JOIN[6]-GBY[8]-RS[9]-GBY[10]-FIL[20]-RS[14]-JOIN[17]-SEL[18]-FS[19]

可以看到在GBY[10]-FIL[20]-SEL[11]-RS[14]中,SEL[11]被去掉了。

单步调试语句的优化过程,由于优化步骤有21个,使用二分法检查到底是运行到哪一个优化器把SEL去掉了。

最终发现是 IdentityProjectRemover 优化器。

这个优化器的功能是根据前后输入输出,去掉不必要的SELECT Operator。检查到这里,没有继续往下查了,还有一些为什么FIL[20] row schema为什么是两个字段,但实际输出内容却不是的问题。

搜索

使用关键词IdentityProjectRemover搜索源代码,看看这个代码最新版本有没有修改过。搜索发现一些issue,找到了关闭这个优化器的参数。hive.optimize.remove.identity.project=false,另外发现在hive1.2.0版本,增加了另一个参数,间接默认关闭了这个优化。

另外发现这个issue和我们这次的错误类似,应该是同一个问题 HIVE-10996 。这个issue里面有说明更详细的错误原因,见评论

这个issue已经有补丁,但目前还没有合并到主干。它的解决的方法是在GBY-FIL-SEL中插入一个SEL,变成GBY-SEL-FIL-SEL

当前解决方案

设置参数 hive.optimize.remove.identity.project=false 关闭这个优化器

这个优化是从1.1.0版本引入(我们当前使用的版本)

技巧

刚开始debug的时候很慢,而且越用越慢,后来发现限制hive客户端使用内存有一定效果。可能debug时需要dump内存。

使用 hive –debug 可以远程debug

使用 hive –hiveconf hive.root.logger=DEBUG,console 可以临时打印日志




fatkun