当前位置: 首页 > news >正文

Hive-因精度丢失导致的 join 数据异常

一、问题复现

不知你是否遇到过 join 结果明显不匹配的情况,例如on t1.join_key = t2.join_key中两个join_key明显不相等,但 join 的结果却将其匹配在一起。今日博主在通过用户 id 关联获取用户信息时发现一个用户 id 可以在用户维表中匹配出若干条(用户维表不存在数据重复),如下:

-- 业务表
create table tmp_hz_perm.tmp_20240520_1
(id string
) stored as parquet;-- 用户维度表
create table tmp_hz_perm.tmp_20240520_2
(id   bigint,name string
) stored as parquet;

插入若干条数据

insert into tmp_hz_perm.tmp_20240520_1
values ('4268348961309240666');insert into tmp_hz_perm.tmp_20240520_2
values (4268348961309240666, 'user1'),(4268348961309241004, 'user2'),(3268348961319241004, 'user3');

模拟事故 sql

-- sql-1
select *
from tmp_hz_perm.tmp_20240520_1 t1
left join tmp_hz_perm.tmp_20240520_2 t2 on t1.id = t2.id;

我们期望的结果是返回user1,但实际情况却是匹配出多条数据

image-20240521162228139

有经验的小伙伴可能一眼就看出来 join 的问题,那就是两个join_key数据类型不一致,恭喜你成功找到了这个问题!!!那么对应的解决方案就是保持数据类型一致即可

-- sql-2
select *
from tmp_hz_perm.tmp_20240520_1 t1
left join tmp_hz_perm.tmp_20240520_2 t2 on cast(t1.id as bigint) = t2.id;

结束了吗???显然没有!我们还没有探寻这个问题的本质

二、本质分析

上面的现象可以总结出两点疑问:

  1. 数据不一致真的查询不出来数据吗
  2. 为什么会关联出一条完全不相干的数据

对于问题一,数据不一致是可以查询出来的,例如

-- sql-3
select * from tmp_hz_perm.tmp_20240520_2 where id = '4268348961309240666';+----------------------+----------------------+
|  tmp_20240520_2.id   | tmp_20240520_2.name  |
+----------------------+----------------------+
| 4268348961309240666  | user1                |
+----------------------+----------------------+
1 row selected (0.145 seconds)

回答问题二需要从执行计划出发

STAGE DEPENDENCIES:Stage-4 is a root stageStage-3 depends on stages: Stage-4Stage-0 depends on stages: Stage-3STAGE PLANS:Stage: Stage-4Map Reduce Local WorkAlias -> Map Local Tables:t2 Fetch Operatorlimit: -1Alias -> Map Local Operator Tree:t2 TableScanalias: t2Statistics: Num rows: 3 Data size: 6 Basic stats: COMPLETE Column stats: NONEHashTable Sink Operatorkeys:0 UDFToDouble(id) (type: double)1 UDFToDouble(id) (type: double)Stage: Stage-3Map ReduceMap Operator Tree:TableScanalias: t1Statistics: Num rows: 1 Data size: 1 Basic stats: COMPLETE Column stats: NONEMap Join Operatorcondition map:Left Outer Join 0 to 1keys:0 UDFToDouble(id) (type: double)1 UDFToDouble(id) (type: double)outputColumnNames: _col0, _col4, _col5Statistics: Num rows: 3 Data size: 6 Basic stats: COMPLETE Column stats: NONESelect Operatorexpressions: _col0 (type: string), _col4 (type: bigint), _col5 (type: string)outputColumnNames: _col0, _col1, _col2Statistics: Num rows: 3 Data size: 6 Basic stats: COMPLETE Column stats: NONEFile Output Operatorcompressed: falseStatistics: Num rows: 3 Data size: 6 Basic stats: COMPLETE Column stats: NONEtable:input format: org.apache.hadoop.mapred.SequenceFileInputFormatoutput format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormatserde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDeExecution mode: vectorizedLocal Work:Map Reduce Local WorkStage: Stage-0Fetch Operatorlimit: -1Processor Tree:ListSink

注意 hive 的执行计划中比我们想象中要多做一步UDFToDouble,其原因就是当两个关联键数据不一致时为了还可以进行关联,hive 将其 key 统一转换为Double,同时也可以看一下UDFToDouble的处理逻辑

public DoubleWritable evaluate(LongWritable i) {if (i == null) {return null;} else {doubleWritable.set(i.get());return doubleWritable;}
}public DoubleWritable evaluate(Text i) {if (i == null) {return null;} else {if (!LazyUtils.isNumberMaybe(i.getBytes(), 0, i.getLength())) {return null;}try {doubleWritable.set(Double.parseDouble(i.toString()));return doubleWritable;} catch (NumberFormatException e) {// MySQL returns 0 if the string is not a well-formed double value.// But we decided to return NULL instead, which is more conservative.return null;}}
}// doubleWritable.set(...)
public void set(double value) {this.value = value;
}

可以看出set入参均是 double,那么4268348961309240666在进行数据转换时一定会发生精度丢失(远超 double 的范围),下面的一个小 demo 可以很好的解释为什么会匹配出不相等的数据

package fun.uhope;import org.apache.hadoop.hive.ql.udf.UDFToDouble;
import org.apache.hadoop.hive.serde2.io.DoubleWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;public class Test {public static void main(String[] args) {String k1 = "4268348961309240666";long k2 = 4268348961309240666L;long k3 = 4268348961309241004L;long k4 = 3268348961309241004L;UDFToDouble uDFToDouble1 = new UDFToDouble();UDFToDouble uDFToDouble2 = new UDFToDouble();UDFToDouble uDFToDouble3 = new UDFToDouble();UDFToDouble uDFToDouble4 = new UDFToDouble();DoubleWritable v1 = uDFToDouble1.evaluate(new Text(k1));DoubleWritable v2 = uDFToDouble2.evaluate(new LongWritable(k2));DoubleWritable v3 = uDFToDouble3.evaluate(new LongWritable(k3));DoubleWritable v4 = uDFToDouble4.evaluate(new LongWritable(k4));System.out.println(v1);System.out.println(v2);System.out.println(v3);System.out.println(v4);System.out.println(v1.compareTo(v2));System.out.println(v1.compareTo(v3));System.out.println(v1.compareTo(v4));System.out.println((double) k2 == (double) k3);System.out.println((double) k2 == (double) k4);System.out.println(k2 == k3);}
}

结果如下:

image-20240521165433637

对于sql-2sql-3各位可以查看一下各自的执行计划就能明白为什么可以得到期望的结果

思考: 针对 hive join 过程中当数据类型不一致时采用UDFToDouble是否合理

相关文章:

  • STL用法总结
  • 简单说说我对集成学习算法的一点理解
  • 【实战教程】构建可复用的 Spring Boot starter 微服务组件
  • Android 使用kotlin Retrofit2 + Dagger2完成网络请求跟依赖注入组合使用
  • 对未知程序所创建的 PDF 文档的折叠书签层级全展开导致丢签的一种解决方法
  • Python疑难杂症--考试复习
  • c++学习----初识类和对象(上)
  • 1882java密室逃脱管理系统 Myeclipse开发mysql数据库web结构java编程计算机网页项目
  • 揭秘小程序商城的团购奇迹:独特模式引领盈利新纪元
  • Python代码:二十八、密码游戏
  • 搜狐视频专访神工坊创始人任虎:以先进计算技术为引擎,引领新一代CAE革新之路
  • IC开发——VCS基本用法
  • ImportError: cannot import name ‘packaging‘ from ‘pkg_resources‘‘
  • 从CSV到数据库(简易)
  • 深入URP之Shader篇16: UNITY_BRANCH和UNITY_FLATTEN
  • hadoop入门学习教程--DKHadoop完整安装步骤
  • HomeBrew常规使用教程
  • input实现文字超出省略号功能
  • iOS动画编程-View动画[ 1 ] 基础View动画
  • Java 实战开发之spring、logback配置及chrome开发神器(六)
  • Promise面试题2实现异步串行执行
  • Sass Day-01
  • Vue UI框架库开发介绍
  • Vue.js-Day01
  • WePY 在小程序性能调优上做出的探究
  • 多线程 start 和 run 方法到底有什么区别?
  • 仿天猫超市收藏抛物线动画工具库
  • 来,膜拜下android roadmap,强大的执行力
  • 如何使用Mybatis第三方插件--PageHelper实现分页操作
  • 十年未变!安全,谁之责?(下)
  • 问题之ssh中Host key verification failed的解决
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 如何在招聘中考核.NET架构师
  • 昨天1024程序员节,我故意写了个死循环~
  • ###C语言程序设计-----C语言学习(6)#
  • #LLM入门|Prompt#1.7_文本拓展_Expanding
  • #控制台大学课堂点名问题_课堂随机点名
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • (02)vite环境变量配置
  • (6)【Python/机器学习/深度学习】Machine-Learning模型与算法应用—使用Adaboost建模及工作环境下的数据分析整理
  • (ctrl.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (webRTC、RecordRTC):navigator.mediaDevices undefined
  • (动手学习深度学习)第13章 计算机视觉---微调
  • (附源码)springboot助农电商系统 毕业设计 081919
  • (四)JPA - JQPL 实现增删改查
  • (一)Thymeleaf用法——Thymeleaf简介
  • (转) Android中ViewStub组件使用
  • (转)Mysql的优化设置
  • **PHP二维数组遍历时同时赋值
  • .NET 3.0 Framework已经被添加到WindowUpdate
  • .NET C# 使用 SetWindowsHookEx 监听鼠标或键盘消息以及此方法的坑
  • .NET Core实战项目之CMS 第十二章 开发篇-Dapper封装CURD及仓储代码生成器实现
  • .NET/C# 避免调试器不小心提前计算本应延迟计算的值
  • .NET大文件上传知识整理