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去掉了。 优化前

<span lang="EN-US">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]</span>
<span lang="EN-US">ppd.PredicatePushDown: After PPD</span>
<span lang="EN-US">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]</span>
优化后
<span lang="EN-US">TS[0]-FIL[21]-RS[3]-JOIN[6]-GBY[8]-RS[9]-GBY[10]-FIL[20]-RS[14]-JOIN[17]-SEL[18]-FS[19]</span>
可以看到在GBY[10]-FIL[20]-SEL[11]-RS[14]中,SEL[11]被去掉了。 单步调试语句的优化过程,由于优化步骤有21个,使用二分法检查到底是运行到哪一个优化器把SEL去掉了。 最终发现是 IdentityProjectRemover 优化器。 这个优化器的功能是根据前后输入输出,去掉不必要的SELECT Operator。检查到这里,没有继续往下查了,还有一些为什么FIL[20] row schema为什么是两个字段,但实际输出内容却不是的问题。 ### 搜索

使用关键词IdentityProjectRemover搜索源代码,看看这个代码最新版本有没有修改过。搜索发现一些issue,找到了关闭这个优化器的参数。<span lang="EN-US">hive.optimize.remove.identity.project=false</span>,另外发现在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 可以临时打印日志
updatedupdated2023-12-062023-12-06