数据测试实践从一个bug开始的大数据引擎兼容性探索
作者:京东零售 李晓洁
我们常常忘记,天才也取决于其所能掌握的数据,即使阿基米德也无法设计出爱迪生的发明。——Ernest Dimnet
在大数据时代,精准而有效的数据对于每个致力于长期发展的组织来说都是重要资产之一,而数据测试更是不可或缺的一部分。数据测试不仅关注数据加工的代码逻辑,还要考虑大数据执行引擎带来的影响,因为各种引擎 框架 将对同一份数据产生不同的计算或检索结果。本文将从一个年度账单bug引入,讲解在数据测试实践中对大数据执行引擎兼容性差异的探索。一、需求内容
京东-我的京东-年度账单是一年一次,以 用户视角 对在平台一年的消费情况进行总结。账单从购物,权益,服务等方面切入,帮助用户挖掘在自我难以认知的数据角度,通过这种方式让用户从账单中发掘打动内心的立意,并主动进行分享和传播。本次,我京年度账单以"2022购物印象"为主题,通过不同的数据维度组成村落故事线,用户以虚拟人物形象贯穿始终,用户浏览完故事线后,可生成购物印象。
年度账单其中一个报表为用户年度购买的小家电品类。该报表使用年度账单汇总表中的 小家电品类集合 字段,计算了2022年度某用户全年最后购买的两款小家电所在的品类。本文bug分享将围绕这个字段展开。
表名
app_my_jd_user_bill_year_sum (用户年度账单汇总表)
字段名
small_electrical_appliance_list 小家电品类集合
数据类型
String
数据描述
2022年中,用户全年最后购买的两款小家电所在的品类清单
取数方式
按照下单时间倒序拍组,输出全年最后购买的2个小家电品类;输出的2个品类间用|分隔。
数据源
adm_my_jd_user_bill_month (用户月度账单明细表)
二、 缺陷描述
缺陷描述:在APP层用户年度账单汇总模型app_my_jd_user_bill_year_sum中,对于小家电品类集合字段,APP表结果与手动计算结果不一致。
以用户" Mercury "、"乐乐1024 "、"活力少年 "的购买数据为例,上游ADM层以 array 类型存储用户每月购买的小家电相关品类,如下图所示:
•根据小家电品类集合字段定义,APP层应取这三个用户全年最后购买的2个品类,即 "Mercury" 在2022年11月购买的VR头戴显示器、电炒锅,"乐乐1024" 在2022年10月购买的冲牙器、空气净化器,"活力少年" 在2022年10月购买的VR头戴显示器、电炒锅。因此,经手动计算,APP层正确计算结果应为:
•而APP层年度账单汇总表中的小家电集合品类如下,结果错误,不符合预期结果。
三、 缺陷排查过程1. 执行引擎兼容差异
测试排查中,首先发现了Hive和Spark引擎之间的语法兼容差异。
•当使用APP层脚本中小家电品类集合口径构建SQL,手动对上游表执行查询时发现,Hive引擎得到的集合有序,执行结果正确:
•使用Spark引擎执行查询时,集合乱序,执行结果错误:
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 coerceTypeBase(Type sourceType, String resultTypeBase) 方法中,但对数据类型的要求更为严格。一些在Hive中常见的数字与字符串进行比较的查询语句,Presto会直接抛类型不一致的错误。
◦下图为Hive和Presto的隐式转换规则,蓝色区域是Presto和Hive都支持的类型转换,绿色区域是Presto不支持但是Hive支持的类型转换,红色区域是两者都不支持的类型转换。可以看到,hive的隐式转换更为广泛,而presto尤其在字符类型的隐式转换中更为严格。
•隐式转换示例: --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
甄子丹在搜救中告诉你教育孩子是科学,可不能一骂了之这个十一假期,甄子丹给我上了一节亲子教育课!由罗志良执导,甄子丹监制甄子丹韩雪贾冰主演的电影搜救,目前已经开启点映,并于10月3日全国公映。刚从电影院出来的我表示搜救是今年十一档电
活下去也是一种能力有些人因遭遇不可抗拒的压力,如病如祸如灾总之,自认为无法逆转,而采取提前结束自已的生命。这种状况,说白了还是缺失一种能力。缺失什么能力?缺失为他人为自已负责的能力!生命来之不易,拥
女孩子真的应该好好爱自己1女人要好好爱自己,善待自己的身体,善待他人,知足常乐,幸福就会不请自来。2从今天开始,善待自己,好好生活,爱唱爱跳爱旅游的女人,不可能一蹶不振。3女人要在变老的道路上,善待自己过
这三年,其实幸福也简单朋友问我,三年疫情,对你来说最大的变化的是什么。放在一年前,两年前,甚至半年前三个月前,我可能都会说一堆深刻的,或者大家都会有普遍认知的变化,但现在,突然觉得那些都不值得一说了,因
今明两年准备买房的家庭,不妨先听听这6条忠告,确实很有用十一假期已经过半,但是从目前的市场行情来看,金九银十已经成为过去式。而在3年前,每年的十一假期都是一年之中楼市行情最好的时候,人们在假期期间的购房欲望非常强烈。而今年的假期,虽然开
拽着幸福奔跑很久都没有用文字记录自己的心情了。在今天,有一些感触,觉得有必要写出来。一直以来,都习惯于写实。所以继续关于自己,无情的暴露,真实的写实。虽然,我知道文学作品来源于生活,但必经淬炼
胡蝶31岁嫁给大12岁的陆川,一个人产检和带娃,却说自己很幸福文丨柠一编辑丨娱阿姨近几年特别流行一个词,叫丧偶式育儿。意思是在养育孩子的过程中,只有母亲一个人既出钱又出力,父亲却对此不管不顾,跟家里没有这个人一样。在大多数人眼里,只有普通人才
好命之人,一看便知你的命好不好,从嘴巴就能看出来了好好说话,十分的重要,有时候少一句拆台,多一句赞美。也许事情就会走向不同的结局。口者,心之门户也。好命之人,一看便知,你的命好不好,其实从嘴巴就能够看出来了。01。你说什么样的话,
视角冲击感很强的五座在建企业总部格力中化OPPO碳云腾讯前言格力本文指珠海格力电器股份有限公司,总部位于珠海,全球空调龙头企业,位居财富世界五百强企业榜第487名。中化本文是指中国中化集团有限公司,是一家央企,位居财富世界五百强企业榜第
2024年起,美国将会全面禁用中国产电池?比亚迪高管做出回应在时代不断发展的过程中,除了传统的行业之外,也催生出了很多新兴行业,而且不同的时代所对应的发展要求也不一样,这就导致了行业之间的转型升级和更新迭代。就目前来说,新能源汽车虽然处于初
长续航不妥协2K元高性价比手机推荐2000元是很多消费者买手机的价格段,但是在这个价格段每一位使用者的需求都不一样,无论是性能还是拍照,又或者是续航都希望自己买到的产品更为全面性价比更高。针对此需求小编挑选了四款价