线上数据问题排查案例分享-因为 HMS 和底层 orc 文件中某字段的数据精度不一致造成的数据丢失问题

大家好,我是明哥,本篇文章跟大家分享一次线上数据问题的排查案例,并总结下背后的技术背景和最佳实践,希望对大家有所帮助。

1. 避坑指南和最佳实践

先说下通过本案例总结的避坑指南和最佳实践。

  • hive 的元数据服务 hms 和表底层的 orc 文件中都存储了表的元数据信息,两者天然是割裂的,当两者信息不一致时,容易出现各种奇怪的数据问题;
  • 为避免潜在的数据问题,开发人员需要确保 hms 和 orc 文件中元数据信息的一致,为此应尽量避免更改 hive orc 表字段的数据类型(尤其需要避免对已存储了数据的 orc 表字段做数据类型不兼容的修改);
  • 为避免潜在的数据问题,使用同步工具如 datax 等直接同步数据到 orc 文件时,orc 文件也应该尽量使用对应表在 HMS 中定义的数据类型和数据精度来进行数据同步;
  • 为避免潜在的数据问题,使用 SQL 语句进行数据查询操作时,在数据类型不一致时,应尽量使用 CAST 进行显示转换而不依赖 HIVE SQL 的隐式转换机制;

2. 问题现象

某客户现场部分 HIVE ORC 表在各层的数据条数不一致,存在数据丢失问题。

比如对于 hive orc 表 hs_sr.sr_xxx/hs_ods.ods_xxx/hs_tmp.tmp_pxxx, 使用 count(1) 查询,发现 hs_sr 和 hs_ods 记录条数一致,但 hs_tmp 层则缺少了400条左右数据。

进一步查询发现,指定条件如 prod_code 查询某条数据时,在 hs_sr 层可以查到该条数据但在 hs_tmp 层查不到该条数据,而在 hs_ods 层查询该条数据时, 只要查询字段列表中包含 prod_scale 字段就会查不到该条数据,而查询字段列表不包含 prod_scale 字段就可以查询到该条数据。

备注:
*本案例中,数据链路为使用 datax 从上游交易系统 oracle 采集到 hive 中的 hs_sr 层,然后使用 hive sql 对 hs_sr 层进行加工处理后写入到 hs_ods 层,再经过 hive sql 进一步加工处理后写到 hs_tmp 层。


3. 问题原因-概述

简单来讲,问题原因是 prod_scale 字段在hive表中的数据类型和数据精度跟hive表底层 orc 文件中的数据类型和数据精度不一致,当 orc文件中部分记录 prod_scale 字段实际存储的值大于 hive 中定义的该字段的精度时,hive 查询该字段并写入到下游 ORC 表时会隐式转换为 NULL 值,而部分版本的 HIVE 在将 NULL 值写入 ORC 文件时有 BUG,会造成底层 ORC 文件中部分记录该字段不可读,所以后续 hive 再次在有问题的ORC文件中查询问题字段并插入最下游的 ORC 表时,下游表中就缺失了部分记录。。

4. 解决方案

解决方案按照思路不同分为两种,一种是业务侧的调整,一种是平台侧/工具侧的调整。

4.1 业务侧调整

业务侧调整,可以调整 hive 中相关表相关字段在 DDL 定义中的数据类型和数据精度,使之与上游交易系统 ORALCE 中相关字段的数据类型和数据精度一致,比如这里发现的 prod_scale 字段应从 decimal(15,4) 调整为 decimal(18,4);(查询上游交易系统 oracle 中的数据,发现确实有部分记录的 prod_scale 字段值比 decimal(15,4)精度更大,且业务确认这部分数据是正常数据,故应该调整 HIVE 中字段的数据类型和数据精度与上游一致);

4.2 平台侧/工具侧调整

本案例中使用了 datax 从上游 oralce 同步数据到 hive orc 表,故可以对 datax 同步作业进行调整:datax 同步数据时,数据类型和数据精度应该以目标 HIVE 表中定义的数据类型和数据精度为准,对于 decimal 字段也应该以目标hive表中的定义为准,而不是全部使用默认的最大精度 decimal(38,18),从而避免 orc 文件和 hms 中相关元数据信息的不一致,减少后续数据类型隐式转换等操作带来的潜在问题。(datax 同步作业调整后,在数据同步过程中,如果遇到上游 ORACLE 数据精度大于HIVE目标表的数据精度,同步作业会报错退出,从而能第一时间暴露数据问题确保数据质量,而不会将数据质量问题带到下游)。

备注:
**由于开源的 datax 并不支持 hive 的 decimal 数据类型,而
金融行业对数据准确性普遍要求较高,故我司内部对 datax 做了增强,支持 hive 的 decimal 数据类型。**

5. 问题原因-技术细节

  • 本案例中数据链路如下:使用 datax 从上游交易系统 oracle 采集到 hive 中的 hs_sr 层,然后使用 hive sql 对 hs_sr 层进行加工处理后写入到 hs_ods 层,再经过 hive sql 进一步加工处理后写到 hs_tmp 层;
  • 本案例中,hive 中 hs_sr/hs-ods/hs_tmp 层相关表中关键字段 prod_scale 都被定义为了decimal(15,4), 而上游 ORACLE 中该字段类型为 decimal(18,4);
  • orc 文件:采集上游 oracle 表数据并写入 hive orc 表时,对于 decimal 类型的字段,当前 datax 作业使用的都是 hive 默认的最大精度即 decimal(38,18),所以所有记录所有字段无论精度大小都能被同步到 ORC 文件中;
  • hs_sr 层表:但 HIVE 的 hs_sr 层表中该字段被定义为了更小精度 decimal(15,4),跟底层orc文件中的 decimal(38,18) 并不一致,所以 hive 查询该字段时就会隐式转换为目标类型 decimal(15,4),此时更大精度的值会被转换为 NULL 值进行处理,所以 hs_sr 层的表查询时不会缺少记录,但大精度的字段值会被显示为 NULL;
  • hs_ods 层表:在查询 hs_sr 层表数据再写入 hs_ods 层表时,由于上述隐式转换的原因,部分大精度字段值会被转换为 NULL 值进行处理,而部分版本的 HIVE 在将 NULL 值写入 ORC 文件时有 BUG,会造成底层 ORC 文件中部分记录该字段不可读,具体哪些记录该字段受到影响跟底层 orc 文件中对应的 stripe 中存储了哪些记录的该字段有关,所以 hs_ods 层表只要查询字段列表中包含 prod_scale 字段就会查不到该条数据,而查询字段列表不包含 prod_scale 字段就可以查询到该条数据,同时使用 count(1) 也会发现数据条数没有缺失;
  • hs_tmp 层表:后续查询 hs_ods 层表数据并写入 hs_tmp 层表时,由于 hs_ods 层表底层的 orc 文件中部分 stripe 有问题,所以当查询字段列表包含 prod_scale 字段时,对应 stripe 中的记录都会受到影响查询不到,最终插入 hs_tmp 层的数据也就缺失了这部分记录,使用 count(1) 也会发现数据条数有缺失,即最终出现了数据丢失问题;

6. 技术背景

  • ORC 文件是自描述格式,orc 和 hms 中都存储了表的元数据信息,所以二者天然是割裂的;
  • 当 hive 查询 ORC 表数据时,如果 orc 文件和 hms 中字段类型元数据信息不一致就会涉及到隐式转换;
  • HIVE 并不是 ANSI dialect compliant 的,即 HIVE 并不符合 ANSI SQL 标准,在处理无效数据时会返回NULL值而作业不会报错;
  • hive 中 decimal 类型的默认精度也即最大精度是 decimal(38,18), 当 decimal 类型的字段值在转换为更小精度比如 decimal(15,4) 时,较大精度的值会被转换为 NULL 值(隐式和显示转换都是如此);
  • 部分老版本的 hive 写 ORC 文件时在处理隐式转换的 NULL 值时有 BUG,会造成底层写入的 ORC 文件中的部分记录该字段不可读,但作业不会报错,具体哪些记录受到影响跟底层 orc 文件中对应 stripe 存储了哪些记录该字段有关;
  • 可以使用工具如 hive --orcfiledump xx 来验证表底层 orc 文件的完整性;
  • 相关JIRA

[https://issues.apache.org/jir...
](https://issues.apache.org/jir...)


Michael_Li
1 声望1 粉丝