数据测试实践:从一个bug开始的大数据引擎兼容性探索

京东云开发者
• 阅读 385

作者:京东零售 李晓洁

我们常常忘记,天才也取决于其所能掌握的数据,即使阿基米德也无法设计出爱迪生的发明。——Ernest Dimnet

在大数据时代,精准而有效的数据对于每个致力于长期发展的组织来说都是重要资产之一,而数据测试更是不可或缺的一部分。数据测试不仅关注数据加工的代码逻辑,还要考虑大数据执行引擎带来的影响,因为各种引擎框架将对同一份数据产生不同的计算或检索结果。本文将从一个年度账单bug引入,讲解在数据测试实践中对大数据执行引擎兼容性差异的探索。

一、需求内容

京东-我的京东-年度账单是一年一次,以用户视角对在平台一年的消费情况进行总结。账单从购物,权益,服务等方面切入,帮助用户挖掘在自我难以认知的数据角度,通过这种方式让用户从账单中发掘打动内心的立意,并主动进行分享和传播。本次,我京年度账单以“2022购物印象”为主题,通过不同的数据维度组成村落故事线,用户以虚拟人物形象贯穿始终,用户浏览完故事线后,可生成购物印象。

年度账单其中一个报表为用户年度购买的小家电品类。该报表使用年度账单汇总表中的小家电品类集合字段,计算了2022年度某用户全年最后购买的两款小家电所在的品类。本文bug分享将围绕这个字段展开。

数据测试实践:从一个bug开始的大数据引擎兼容性探索

二、 缺陷描述

缺陷描述:在APP层用户年度账单汇总模型app_my_jd_user_bill_year_sum中,对于小家电品类集合字段,APP表结果与手动计算结果不一致。

以用户'Mercury'、'乐乐1024'、'活力少年'的购买数据为例,上游ADM层以array<string>类型存储用户每月购买的小家电相关品类,如下图所示:

数据测试实践:从一个bug开始的大数据引擎兼容性探索

• 根据小家电品类集合字段定义,APP层应取这三个用户全年最后购买的2个品类,即'Mercury'在2022年11月购买的VR头戴显示器、电炒锅,'乐乐1024'在2022年10月购买的冲牙器、空气净化器,'活力少年'在2022年10月购买的VR头戴显示器、电炒锅。因此,经手动计算,APP层正确计算结果应为:

数据测试实践:从一个bug开始的大数据引擎兼容性探索

• 而APP层年度账单汇总表中的小家电集合品类如下,结果错误,不符合预期结果。

数据测试实践:从一个bug开始的大数据引擎兼容性探索

三、 缺陷排查过程

1. 执行引擎兼容差异

测试排查中,首先发现了Hive和Spark引擎之间的语法兼容差异。

• 当使用APP层脚本中小家电品类集合口径构建SQL,手动对上游表执行查询时发现,Hive引擎得到的集合有序,执行结果正确:

数据测试实践:从一个bug开始的大数据引擎兼容性探索

• 使用Spark引擎执行查询时,集合乱序,执行结果错误:

数据测试实践:从一个bug开始的大数据引擎兼容性探索

2. 脚本梳理

缺陷原因为集合乱序导致的取数错误。每个用户在上游ADM存在12个数组对应12个月购买小家电品类的集合,需要集合函数(collect)将12个月分组数据倒序排序,汇合成1个列表,然后取列表前两个元素。

HQL提供两种分组聚合函数:collect_list()collect_set(),区别在于collect_set()会对列表元素去重。由于用户不同月购买的品类集合可能重复,因此脚本使用了collect_set()

然而collect_set()将导致集合乱序,集合中元素不再按月份倒序排列,取出List[0]和List[1]不是用户全年最后购买的两个小家电品类。

SELECT
    user_pin,
    small_electrical_appliance_list,
    concat_ws('|', small_electrical_appliance_list[0], small_electrical_appliance_list[1]) AS small_electrical_appliance
FROM(
    SELECT
        user_pin,
        collect_set(concat_ws(',', small_electrical_appliance_list_split)) AS small_electrical_appliance_list
    FROM(
    SELECT
        dt,
        user_pin,
        small_electrical_appliance_list,
        concat_ws(',', small_electrical_appliance_list) AS small_electrical_appliance
    FROM adm_my_jd_user_bill_month
    WHERE
        dt >= '2022-01'
        AND dt <= '2022-12'
    ORDER BY dt DESC) tmp 
    lateral VIEW explode(SPLIT(small_electrical_appliance, ',')) tmp AS small_electrical_appliance_list_split
    GROUP BY user_log_acct ) 


3. 结论

• 计算脚本逻辑错误,不应使用collect_set()聚合分组。

• 在原生Hive/Spark中,collect_set()函数均无法保证集合有序,而大数据平台Hive对集合计算有序。因此,该脚本在Hive引擎下可以达到生成全年最后购买两个小家电品类的预期目标,但spark引擎则无法得到正确结果。

• Hive执行效率较低,研发通常通过Spark引擎执行,最终导致结果错误。

四、大数据计算引擎兼容差异

1. collect_list()/collect_set() 在hive/spark和presto之间的区别

collect_set()collect_list()在Presto中无法兼容。

• 替代函数:array_agg() (https://prestodb.io/docs/current/functions/aggregate.html?highlight=array_agg#array_agg)

Hive/Spark Presto
collect_list() array_agg()
collect_set() array_distinct(array_agg())

2. 行转列函数在hive和presto之间的区别

• Hive使用lateral VIEW explode()执行行转列的操作,而Presto不支持该函数。这种单列的值转换成和student列一对多的行的值映射.

◦ Hive/Spark query:

lateral VIEW explode(SPLIT(small_electrical_appliance, ',')) tmp AS small_electrical_appliance_list_split

• Presto支持UNNEST来扩展array和map。文档:(https://prestodb.io/docs/current/migration/from-hive.html)

◦ Presto query:

CROSS JOIN UNNEST(SPLIT(small_electrical_appliance, ',')) AS small_electrical_appliance_list_split;

3. 隐式转换在引擎之间的区别

• Hive/Spark支持包括字符串类型到数字类型在内的多种隐式转换,如将字符串'07'转化为数字7,然后进行比较操作。

◦ Hive隐式转换规则:详见链接 Allowed Implicit Conversions

• 虽然Presto也有自己的一套隐式类型转换规则包含在public Optional<Type> coerceTypeBase(Type sourceType, String resultTypeBase)方法中,但对数据类型的要求更为严格。一些在Hive中常见的数字与字符串进行比较的查询语句,Presto会直接抛类型不一致的错误。

◦ 下图为Hive和Presto的隐式转换规则,蓝色区域是Presto和Hive都支持的类型转换,绿色区域是Presto不支持但是Hive支持的类型转换,红色区域是两者都不支持的类型转换。可以看到,hive的隐式转换更为广泛,而presto尤其在字符类型的隐式转换中更为严格。

数据测试实践:从一个bug开始的大数据引擎兼容性探索

• 隐式转换示例:

--Hive/Spark隐式转换
'07' >= 6 -- true (CAST('07' AS DOUBLE) >= CAST(6 AS DOUBLE))
'test' <> 1 -- NULL
'1' = 1.0 -- true

--Presto隐式转换
'07' >= 6 -- false (CAST('07' AS Varchar) >= CAST(6 AS Varchar))
'test' <> 1 -- true
'1' = 1.0 -- ERROR:io.prestosql.spi.PrestoException: Unexpected parameters (varchar(1), decimal(2,1)) for function $operator$equal. Expected: $operator$equal(T, T) T:comparable


点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
MySQL数据库表设计规范
一、数据库设计1、一般都使用INNODB存储引擎,除非读写比率<1%,才考虑使用MYISAM存储引擎;其他存储引擎请在DBA的建议下使用。2、Storedprocedure(包括存储过程,函数,触发器)对于MYSQL来说还不是很成熟,没有完善的出错记录处理,不建议使用。3、UUID(),USER()这样的
Wesley13 Wesley13
3年前
mysql存储引擎
什么是存储引擎?MySQL中的数据用各种不同的技术存储在文件(或者内存)中。这些不同的技术以及配套的相关功能在MySQL中被称作存储引擎(也称作表类型)。MySql的存储引擎:MyISAM节省数据库空间,当数据读远大于修改时,可以使用该存储引擎InnoDB支持事务,
Stella981 Stella981
3年前
Scrapy框架全
架构介绍Engine:引擎,处理整个系统的数据流处理,触发事物,是整个框架的核心Item:项目,他定义了爬取结果的数据结构,爬取的数据会被赋值成该Item对象Scheduler:调度器,接受引擎发过来的请求并将其加入到队列中,在引擎再次请求的时候将请求提供给引擎Downloader:下载器,下载网页内容,并将网页内容返回给spider
Wesley13 Wesley13
3年前
MySQL索引背后的数据结构及算法原理
摘要本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题。特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BTree索引,哈希索引,全文索引等等。为了避免混乱,本文将只关注于BTree索引,因为这是平常使用MySQL时主要打交道的索引,至于哈希索引和全文索引本文
Stella981 Stella981
3年前
Net Core使用Lucene.Net和盘古分词器 实现全文检索
Lucene.netLucene.net是Lucene的.net移植版本,是一个开源的全文检索引擎开发包,即它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,是一个高性能、可伸缩的文本搜索引擎库。它的功能就是负责将文本数据按照某种分词算法进行切词,分词后的结果存储在索引库中,从索引库检索数据的
Wesley13 Wesley13
3年前
Mysql数据库引擎介绍
一、数据库引擎  数据库引擎是用于存储、处理和保护数据的核心服务。利用数据库引擎可控制访问权限并快速处理事务,从而满足企业内大多数需要处理大量数据的应用程序的要求。使用数据库引擎创建用于联机事务处理或联机分析处理数据的关系数据库。这包括创建用于存储数据的表和用于查看、管理和保护数据安全的数据库对象(如索引、视图和存储过程)。二、数据库引擎任
Stella981 Stella981
3年前
MQ异步同步搜索引擎ElasticSearch数据踩坑
业务背景  在大型网站中,为了减少DB压力、让数据更精准、速度更快,将读拆分出来采用搜索引擎来为DB分担读的压力,ElasticSearch就是目前市面上比较流行的搜索引擎,他的检索速度奇快、支持各种复杂的全文检索,在各种场景下对比其他的搜索引擎的检索速度都显得尤为出众。这篇就先不介绍ElasticSearch了,后续我会出一个ElasticS
Wesley13 Wesley13
3年前
mysql储存引擎
Mysql数据库常用存储引擎数据库存储引擎:是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以获得特定的功能。现在许多不同的数据库管理系统都支持多种不同的数据引擎。MySQL的核心就是插件式存储引擎。
Wesley13 Wesley13
3年前
MySQL数据库存储引擎概述
一、前言引擎(Engine),我们都知道是机器发动机的核心所在,数据库存储引擎便是数据库的底层软件组织。数据库使用数据存储引擎实现存储、处理和保护数据的核心服务。利用数据库引擎可控制访问权限并快速处理事务,从而满足企业内大多数需要处理大量数据的应用程序的要求。使用数据库引擎创建用于联机事务处理或联机分析处理数据的关系数据库。这
探析ElasticSearch Kibana在测试工作中的实践应用 | 京东物流技术团队
一.为什么使用ESKibana离线数据测试中最重要的就是数据验证,一部分需要测试es存储数据的正确性,另一部分就需要验证接口从es取值逻辑的正确性。而为了验证es取值逻辑的正确性,就需要用到Kibana,它能帮助测试同学更加快速高效的执行es数据的查询,大