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 stage
  Stage-3 depends on stages: Stage-4
  Stage-0 depends on stages: Stage-3

STAGE PLANS:
  Stage: Stage-4
    Map Reduce Local Work
      Alias -> Map Local Tables:
        t2 
          Fetch Operator
            limit: -1
      Alias -> Map Local Operator Tree:
        t2 
          TableScan
            alias: t2
            Statistics: Num rows: 3 Data size: 6 Basic stats: COMPLETE Column stats: NONE
            HashTable Sink Operator
              keys:
                0 UDFToDouble(id) (type: double)
                1 UDFToDouble(id) (type: double)
                
  Stage: Stage-3
    Map Reduce
      Map Operator Tree:
          TableScan
            alias: t1
            Statistics: Num rows: 1 Data size: 1 Basic stats: COMPLETE Column stats: NONE
            Map Join Operator
              condition map:
                   Left Outer Join 0 to 1
              keys:
                0 UDFToDouble(id) (type: double)
                1 UDFToDouble(id) (type: double)
              outputColumnNames: _col0, _col4, _col5
              Statistics: Num rows: 3 Data size: 6 Basic stats: COMPLETE Column stats: NONE
              Select Operator
                expressions: _col0 (type: string), _col4 (type: bigint), _col5 (type: string)
                outputColumnNames: _col0, _col1, _col2
                Statistics: Num rows: 3 Data size: 6 Basic stats: COMPLETE Column stats: NONE
                File Output Operator
                  compressed: false
                  Statistics: Num rows: 3 Data size: 6 Basic stats: COMPLETE Column stats: NONE
                  table:
                      input format: org.apache.hadoop.mapred.SequenceFileInputFormat
                      output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat
                      serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
      Execution mode: vectorized
      Local Work:
        Map Reduce Local Work

  Stage: Stage-0
    Fetch Operator
      limit: -1
      Processor 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是否合理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/669570.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Simulink中使用ROS1自定义消息

Simulink中使用ROS1自定义消息 简介前提条件操作流程问题一问题二问题三 吐槽 简介 最近在做的项目里需要使用Simulink与ROS联合仿真,这里就遇到了一个问题,Simulink无法直接使用ROS中的自定义消息,需要在MATLAB中生成一下,再引入…

D3D 顶点格式学习

之前D3D画三角形的代码中有这一句, device.VertexFormat CustomVertex.TransformedColored.Format; 这是设置顶点格式; 画出的三角形如下, 顶点格式是描述一个三维模型的顶点信息的格式;可以包含以下内容, 位置…

C语言:如何在微软VS环境下使用C库?

技术答疑流程 扫描二维码,添加个人微信;支付一半费用,获取答案;如果满意,则支付另一半费用; 知识点费用:10元 项目费用:如果有项目任务外包需求,可以微信私聊

LLM背后的基础模型 1

写在最前面的话 任何开源技术是最有生命力的,也是最具分享精神的。一直觉得大模型领域需要有一个系列能够从零开始系统性的讲述领域知识,给与这个领域的从业人员或者对其有兴趣的门外汉及时的帮助。国外承担“布道者”的公司众多,而数砖公司…

prometheus docker部署

1.安装Docker sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-EOF {"registry-mirrors":["https://hub-mirror.c.163.com"] } EOF export DOWNLOAD_URL"https://hub-mirror.163.com/docker-ce" curl -fsSL https://ge…

python如何安装pyqt4

第一步&#xff0c;下载.whl文件&#xff0c;地址&#xff1a;https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyqt4&#xff0c;这里可以下载不同的python版本对应的包。 第二步&#xff0c;选择一个目录&#xff0c;将下载好的文件放到该目录下&#xff0c;然后cmd下&#xff…

基于IoTDB 平台的学习和研究

Apache IoTDB&#xff08;物联网数据库&#xff09;是一个针对物联网领域的高性能原生数据库&#xff0c;适用于数据管理和分析&#xff0c;并可在边缘计算和云端部署。由于它轻量级的架构、高性能和丰富的功能集&#xff0c;以及与Apache Hadoop、Spark和Flink的深度集成&…

将 vue文件转为字符串在Codemirror里面展示

第一种直接引入 import index from "./FFCesiumExample/basicOperationInterface/addIconMap.vue?raw"; index直接就是字符串。但是出现一个问题就是build的时候可以出警告&#xff0c;。so还有第二种 const readFile (filePath) > {// 创建一个新的xhr对象l…

查看VUE3代理后真正请求的URL

在vite.config.ts中添加如下配置&#xff1a; server: {host: "0.0.0.0", // 指定服务器应该监听哪个 IP 地址port: 8848, // 指定开发服务器端口open: true, // 开发服务器启动时&#xff0c;自动在浏览器中打开应用程序cors: true,// Load proxy configuration fr…

【TB作品】MSP430G2553单片机,智能储物柜

智能储物柜将实现的功能&#xff1a; 1在超市或者机场场景下&#xff0c;用户需要进行物品暂存时。按下储物柜键盘的需求按键&#xff0c;智能储物柜将会随机为用户分配一个还没使用的柜子&#xff0c;屏幕提示用户选择密码存储方式或者身份证存储方式&#xff1b; 2 用户选择密…

【JavaEE进阶】——Mybatis操作数据库(使用注解和XML方式)

目录 &#x1f6a9;三层架构 &#x1f388;JDBC操作回顾 &#x1f6a9;什么是MyBatis &#x1f6a9;MyBatis⼊⻔ &#x1f388;准备工作 &#x1f4dd;创建⼯程 &#x1f4dd;数据准备 &#x1f388;配置数据库连接字符串 &#x1f388;写持久层代码 &#x1f388;单…

【基本数据结构】平衡二叉树

文章目录 前言平衡二叉树1 简介2 旋转2.1 左旋2.2 右旋2.3 何时旋转 3 插入节点4 删除节点5 代码 参考资料写在最后 前言 本系列专注更新基本数据结构&#xff0c;现有以下文章&#xff1a; 【算法与数据结构】数组. 【算法与数据结构】链表. 【算法与数据结构】哈希表. 【…

33【Aseprite 作图】树——拆解

1 树叶 画树叶真累啊&#xff0c;可以先画一个轮廓&#xff0c;细节一点点修 2 1 2 &#xff1b;2 2 2 &#xff08;横着横&#xff09;&#xff0c;这样一点点画树叶 填充颜色&#xff0c;用了喷雾工具 2 树干部分 轮廓部分&#xff0c;左边的是3 3 3 &#xff1b;上下都是…

从不同角度看如何让大模型变得更聪明呢?

算法创新&#xff0c;从代码上优化大模型&#xff0c;可以采取一系列策略来提升其性能和效率。 算法优化&#xff1a;对模型的算法进行精细调整&#xff0c;如改进神经网络架构&#xff0c;使用更高效的层&#xff08;如深度可分离卷积&#xff09;&#xff0c;或者优化递归神经…

前端地图中,已知一个点位,获取相同经度或者纬度下的,某个距离的另一个点位

效果图说明&#xff1a;我在圆的中心点位&#xff0c;找到他某个直线距离的另个一点&#xff0c;标注两者之间的距离。如图所示是25000米。 沿纬度方向移动 在相同经度下&#xff0c;计算沿纬度方向移动1000米的新点位&#xff1a; function calculateLatitudePoint(lat, ln…

回归预测 | MATLAB实现基于GOOSE-LightGBM的多特征输入单输出数据回归预测(鹅优化算法)

回归预测 | MATLAB实现基于GOOSE-LightGBM的多特征输入单输出数据回归预测(鹅优化算法) 目录 回归预测 | MATLAB实现基于GOOSE-LightGBM的多特征输入单输出数据回归预测(鹅优化算法)效果一览基本介绍程序设计参考资料 效果一览 基本介绍 MATLAB实现基于LightGBM算法的数据回归预…

Qt第三方库QicsTable简单实例(1)

闲来无事&#xff0c;无意间看到一个Qics表格操作第三方库&#xff0c;自己写了一个特别简单的实例&#xff0c;效果如图所示&#xff1a; 操作界面的数据还是特别快的&#xff0c;因为使用了模型

java并发处理机制

在Java中&#xff0c;并发处理机制主要是通过线程来实现的。Java提供了丰富的类和接口来支持多线程编程&#xff0c;主要集中在 java.util.concurrent 包中。以下是一些关键的并发处理机制&#xff1a; 1.线程创建&#xff1a;可以通过继承 Thread 类或实现 Runnable 接口来创建…

前端Vue小兔鲜儿电商项目实战Day06

一、本地购物车 - 列表购物车 1. 基础内容渲染 ①准备模板 - src/views/cartList/index.vue <script setup> const cartList [] </script><template><div class"xtx-cart-page"><div class"container m-top-20"><div…

C语言:如何写文档注释、内嵌注释、行块注释?

技术答疑流程 扫描二维码&#xff0c;添加个人微信&#xff1b;支付一半费用&#xff0c;获取答案&#xff1b;如果满意&#xff0c;则支付另一半费用&#xff1b; 知识点费用&#xff1a;10元 项目费用&#xff1a;如果有项目任务外包需求&#xff0c;可以微信私聊