案例分析 | 由Decimal操作计算引发的Spark数据丢失问题

转载自  案例分析 | 由Decimal操作计算引发的Spark数据丢失问题

供稿 | Hadoop Team

编辑 | 顾欣怡

本文3058字,预计阅读时间10分钟

导读

eBay的Hadoop集群上面每天运行着大量Spark计算任务。对于数据计算任务,其计算性能十分重要,数据质量也不可忽视,特别是对于金融数据,数据发生损坏将会产生严重后果。本文分享一次数据质量相关的问题以及我们排查该问题的过程和解决方案。

 

一、症状

一天,金融分析团队的同事报告了一个问题,他们发现在两个生产环境中(为了区分,命名为环境A和B), Spark大版本均为2.3。但是,当运行同样的SQL语句,对结果进行对比后,却发现两个环境中有一列数据并不一致

此处对数据进行脱敏,仅显示发生数据丢失那一列的数据,如下:

由此可见,在环境A中可以查询到该列数据,但是在环境B中却出现了部分数据缺失。

 

二、排查

上述两个查询中用的Spark大版本是一致的,团队的同事通过对比两个环境中的配置,发现有一个参数在最近进行了变更。该参数为:spark.sql.decimalOperations.allowPrecisionLoss, 默认为true。

在环境A中未设置此参数,所以为true,而在环境B下Spark client的spark-defaults.conf中,该参数设置为false。

该参数为PR SPARK-22036 引入,是为了控制在两个Decimal类型操作数做计算的时候,是否允许丢失精度。在本文中,我们就针对乘法这种计算类型做具体分析。

 

关于Decimal类型

在详细介绍该参数之前,先介绍一下Decimal。

Decimal是数据库中的一种数据类型,不属于浮点数类型,可以在定义时划定整数部分以及小数部分的位数。对于一个Decimal类型,scale表示其小数部分的位数,precision表示整数部分位数和小数部分位数之和。

一个Decimal类型表示为Decimal(precision, scale),在Spark中,precision和scale的上限都是38

一个double类型可以精确地表示小数点后15位,有效位数为16位

可见,Decimal类型则可以更加精确地表示,保证数据计算的精度。

例如一个Decimal(38, 24)类型可以精确表示小数点后23位,小数点后有效位数为24位。而其整数部分还剩下14位可以用来表示数据,所以整数部分可以表示的范围是-10^14+1~10^14-1。

 

关于精度和Overflow

关于精度的问题其实我们小学时候就涉及到了,比如求两个小数加减乘除的结果,然后保留小数点后若干有效位,这就是保留精度。

乘法操作我们都很清楚,如果一个n位小数乘以一个m位小数,那么结果一定是一个(n+m)位小数。

举个例子, 1.11 * 1.11精确的结果是 1.2321,如果我们只能保留小数点后两位有效位,那么结果就是1.23。

上面我们提到过,对于Decimal类型,由于其整数部分位数是(precision-scale),因此该类型能表示的范围是有限的,一旦超出这个范围,就会发生Overflow。而在Spark中,如果Decimal计算发生了Overflow,就会默认返回Null值。

举个例子,一个Decimal(3,2)类型代表小数点后用两位表示,整数部分用一位表示,因此该类型可表示的整数部分范围为-9~9。如果我们CAST(12.32 as Decimal(3,2)),那么将会发生Overflow。

下面介绍spark.sql.decimalOperations. allowPrecisionLoss参数。

当该参数为true(默认)时,表示允许Decimal计算丢失精度,并根据Hive行为和SQL ANSI 2011规范来决定结果的类型,即如果无法精确地表示,则舍入结果的小数部分。

当该参数为false时,代表不允许丢失精度,这样数据就会表示得更加精确。eBay的ETL部门在进行数据校验的时候,对数据精度有较高要求,因此我们引入了这个参数,并将其设置为false以满足ETL部门的生产需求。

设置这个参数的初衷是美好的,但是为什么会引发数据损坏呢?

用户的SQL数据非常长,通过查看相关SQL的执行计划,然后进行简化,得到一个可以复现的SQL语句,如下:

 

上面的select语句将会返回一个NULL。

我们将上述语句的执行计划打印出来。

 

执行计划很简单,里面有一个二元操作(乘法),左边的case when 是一个Decimal(34, 24)类型,右边是一个Literal(1)。

程序员都知道,在编程中,如果两个不同类型的操作数做计算,就会将低级别的类型向高级别的类型进行类型转换,Spark中也是如此。

一条SQL语句进入Spark-sql引擎之后,要经历Analysis->optimization->生成可执行物理计划的过程。而这个过程就是不同的Rule不断作用在Plan上面,然后Plan随之转化的过程。

在Spark-sql中有一系列关于类型转换的Rule,这些Rule作用在Analysis阶段的Resolution子阶段。

其中就有一个Rule叫做ImplicitTypeCasts,会对二元操作(加减乘除)的数据类型进行转换,如下图所示:

用文字解释一下,针对一个二元操作(加减乘除), 如果左边的数据类型和右边不一致,那么会寻找一个左右操作数的通用类型(common type), 然后将左右操作数都转换为通用类型。针对我们此案例中的 Decimal(34, 24) 和Literal(1), 它们的通用类型就是Decimal(34, 24),所以这里的Literal(1)将被转换为Decimal(34, 24)。

这样该二元操作的两边就都是Decimal类型。接下来这个二元操作会被Rule DecimalPrecision中的decimalAndDecimal方法处理。

在不允许精度丢失时,Spark会为该二元操作计算一个用来表达计算结果的Decimal类型,其precision和scale的计算公式如下表所示,这是参考了SQLServer的实现。

 

此处我们的操作数都已经是Decimal(34, 24)类型了,所以p1=p2=34, s1=s2=24。

如果不允许精度丢失,那么其结果类型就是 Decimal(p1+p2+1, s1+s2)。由于precision和scale都不能超过上限38,所以这里的结果类型是Decimal(38, 38), 也就是小数部分为38位。于是整数部分就只剩下0位来表示,也就是说如果整数部分非0,那么这个结果就会Overflow。在当前版本中,如果Decimal Operation 计算发生了Overflow,就会返回一个Null的结果。

这也解释了在前面的场景中,为什么使用环境B中Spark客户端跑的结果,非Null的结果中整数部分都是0,而小数部分精度更高(因为不允许精度丢失)。

好了,问题定位到这里结束,下面讲解决方案。

 

三、解决方案

01 合理处理操作数类型

通过观察Spark-sql中Decimal 相关的Rule,发现了Rule DecimalPrecision中的nondecimalAndDecimal方法,这个方法是用来处理非Decimal类型和Decimal类型操作数的二元操作。

此方法代码不多,作用就是前面提到的左右操作数类型转换,将两个操作数转换为一样的类型,如下图所示:

 

文字描述如下:

如果其中非Decimal类型的操作数是Literal类型, 那么使用DecimalType.fromLiteral方法将该Literal转换为Decimal。例如,如果是Literal(1),则转化为Decimal(1, 0);如果是Literal(100),则转化为Decimal(3, 0)。

如果其中非Decimal类型操作数是Integer类型,那么使用DecimalType.forType方法将Integer转换为Decimal类型。由于Integer.MAX_VALUE 为2147483647,小于3*10^9,所以将Integer转换为Decimal(10, 0)。当然此处省略了其他整数类型,例如,如果是Byte类型,则转换为Decimal(3,0);Short类型转换为Decimal(5,0);Long类型转换为Decimal(20,0)等等。

如果其中非Decimal类型的操作是float/double类型,则将Decimal类型转换为double类型(此为DB通用做法)。

因此,这里用DecimalPrecision Rule的nonDecimalAndDecimal方法处理一个Decimal类型和另一个非Decimal类型操作数的二元操作的做法要比前面提到的ImplicitTypeCasts规则处理更加合适。ImplicitTypeCasts 会将Literal(1) 转换为Decimal(34, 24), 而DecimalPrecision将Literal(1)转换为Decimal(1, 0) 。

经过DecimalPrecision Rule的nonDecimalAndDecimal处理之后的两个Decimal类型操作数会被DecimalPrecision中的decimalAndDecimal方法(上文提及过)继续处理。

上述提到的案例是一个乘法操作,其中,p1=34, s1=24, p2 =1, s2=0。

其结果类型为Decimal(36,24),也就是说24位表示小数部分, 12位表示整数部分,不容易发生Overflow。

前面提到过,Spark-sql中关于类型转换的Rule作用在Analysis阶段的Resolution子阶段。而Resolution子阶段会有一批Rule一直作用在一个Plan上,直到这个Plan到达一个不动点(Fixpoint),即Plan不再随Rule作用而改变。

因此,我们可以在ImplicitTypeCasts规则中对操作数类型进行判断。如果在一个二元操作中有Decimal类型的操作数,则此处跳过处理,这个二元操作后续会被DecimalPrecision规则中的nonDecimalAndDecimal方法和decimalAndDecimal方法继续处理,最终到达不动点。

我们向Spark社区提了一个PR SPARK-29000, 目前已经合入master分支。

 

02 用户可感知的Overflow

除此之外,默认的DecimalOperation如果发生了Overflow,那么其结果将返回为NULL值,这样的计算结果异常并不容易被用户感知到(此处非常感谢金融分析团队的同事帮我们检查到了这个问题)。

在SQL ANSI 2011标准中,当算术操作发生Overflow时,会抛出一个异常。这也是大多数数据库的做法(例如SQLService, DB2, TeraData)。

PR SPARK-23179 引入了参数spark.sql. decimalOperations.nullOnOverflow 用来控制在Decimal Operation 发生Overflow时候的处理方式。

默认是true,代表在Decimal Operation发生Overflow时返回NULL的结果。

如果设置为false,则会在Decimal Operation发生Overflow时候抛出一个异常。

因此,我们在上面的基础上合入该PR,引入spark.sql.decimalOperations.nullOnOverflow参数,设置为false, 以保证线上计算任务的数据质量。

 

四、总结

本文分析了一个Decimal操作计算时发生的数据质量问题。我们不仅修复了其不合适的类型转换问题,减小了其结果Overflow的几率,还引入了一个参数,以便在计算发生Overflow时抛出异常,让用户感知到计算中存在的问题,保证线上计算的数据质量。

在大数据计算场景中,我们不仅关心数据计算得快不快,更关心结果数据的质量高不高。这需要各个团队的密切配合,平台开发人员需要提供可靠稳定的计算平台,业务团队需要写出高质量的SQL,数据服务团队则要提供良好的调度和校验服务。相信在各个团队的共同努力下,eBay在大数据这条路上能走得更远、更宽阔。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

入门干货之Electron的.NET实现-Electron.NET

0x01、Electron.NET1、介绍Electron是由Github上的一支团队和一群活跃贡献者维护。用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库。 Electron通过将Chromium和Node.Js合并到同一个运行时环境中,并将其打包为Mac,Windows和Linu…

通过Chocolatey软件包管理器安装.NET Core

在Linux的世界里,有了yum/apt-get百分之九十的软件都可以通过它来安装管理。但是在Windows系统上,装个软件还是挺折腾的。比如我要装个Chrome浏览器,我先得打开IE浏览器吧,我还打不开Chrome的官网吧,得百度吧&#xff…

(十三)RabbitMQ使用详解

RabbitMQ是基于AMQP的一款消息管理系统。AMQP(Advanced Message Queuing Protocol),是一个提供消息服务的应用层标准高级消息队列协议,其中RabbitMQ就是基于这种协议的一种实现。 常见mq: ActiveMQ:基于JMSRabbitMQ:…

ASP.NET Core Web API下事件驱动型架构的实现(二):事件处理器中对象生命周期的管理

在ASP.NET Core Web API下事件驱动型架构的实现(一):一个简单的实现中,我介绍了事件驱动型架构的一种简单的实现,并演示了一个完整的事件派发、订阅和处理的流程。这种实现太简单了,百十行代码就展示了一个…

和某ZYC巨佬和XXY巨佬的随机挑战2总结

前言 一切的起点在那个炎热的酷暑,菜的一批的WYCWYCWYC坐在最容易被∗*∗的左下角。这时他永远都想不到,他与巨佬之间的挑战,即将开始。 正题 规则 随机跳333到蓝题,然后写完。 完成记录 题目博客 T1:P3100−[USACO14JAN]T1:P31…

(十四)消息中间件MQ详解及四大MQ比较

一、消息中间件相关知识 1、概述 消息队列已经逐渐成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能,成为异步RPC的主要手段之一。当今市面上有很多主流的消息中间件,如老牌的ActiveMQ、RabbitMQ&a…

g4e基础篇#3 Git安装与配置

现在你已经对Git有了最基本的了解,现在让我们开始动手开始安装和配置Git环境。Git工具包括Git命令行工具,图形化工具和服务器环境;在我们这个教程中,我们会使用以下软件配置我们的环境:• Windows 操作系统&#xff08…

[认证授权] 6.Permission Based Access Control

在前面5篇博客中介绍了OAuth2和OIDC(OpenId Connect),其作用是授权和认证。那么当我们得到OAuth2的Access Token或者OIDC的Id Token之后,我们的资源服务如何来验证这些token是否有权限来执行对资源的某一项操作呢?比如…

微软发布PowerShell Core第一个版本:支持多平台开发

微软旗下的PowerShell团队正式宣布推出PowerShell Core 6.0,非常诡异的是这明明是Core的第一个版本,但是却用了一个6.0后缀的版本号。“这是我们对PowerShell做出的最大最重要的改变!”微软技术研究员兼PowerShell创始人Jeffrey Snover在Twit…

.NET Core单文件发布静态编译AOT CoreRT

.NET Core单文件发布静态编译AOT CoreRT,将.NET Core应用打包成一个可执行文件并包含运行时。支持Windows, MacOS and Linux x64 w/ RyuJIT codegen。示例项目:https://github.com/dotnet/corert/tree/master/samples/WebApi下面来实际体验。首先确保安装…

2019纪中暑假游记+总结

Travels总篇\texttt{Travels总篇}Travels总篇 7/4\texttt{7/4}7/4 下午才去纪中,早上就一大早和同学出去玩,看了蜘蛛侠然后到3点多才出发。 因为走南沙大桥所以很快就到了(具体有多快忘了,反正路上一点都不塞车)。就愉快的去整理宿舍洗个早…

使用xUnit为.net core程序进行单元测试(上)

一. 导读为什么要编写自动化测试程序(Automated Tests)?可以频繁的进行测试可以在任何时间进行测试,也可以按计划定时进行,例如:可以在半夜进行自动测试。肯定比人工测试要快。可以更快速的发现错误。基本上…

select2删除选中项,allowClear设置

转载自 select2删除选中项,allowClear设置 在使用select2过程中,有时候需要删除我们选中的选项,如下图: 这时候就需要设置select2的allowClear属性了。 有两种方法: 第一种: 直接用select2定义的一个c…

LeetCode算法总结-回溯法与深度优先搜索

转载自 LeetCode算法总结-回溯法与深度优先搜索 回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就…

入门干货之用DVG打造你的项目主页-Docfx、Vs、Github

由于这三项技术涉及到的要点以及内容较多,希望大家有空能自己挖掘一下更多更深的用法。0x01、介绍VS,即VS2017以及以上版本,宇宙最好的IDE,集成了宇宙最有前景的平台,前阶段也支持了宇宙最好的语言。Github&#xff0c…

ASP.NET Core中使用IOC三部曲(一.使用ASP.NET Core自带的IOC容器)

前言本文主要是详解一下在ASP.NET Core中,自带的IOC容器相关的使用方式和注入类型的生命周期.这里就不详细的赘述IOC是什么 以及DI是什么了.. emm..不懂的可以自行百度.正文今天我们主要讲讲如何使用自带IOC容器,emm..虽然自带的功能不是那么强大,但是胜在轻量级..而且..不用引…

P4130,jzoj1214-[NOI2007]项链工厂【线段树】

正题 题目链接:https://www.luogu.org/problemnew/show/P4130 题目大意 一个环形颜色珠子链,位置(注意不是上面的珠子)从最上顺时针下来位置依次标号1∼n1\sim n1∼n。 然后要求支持以下操作 Rk:R\ k:R k:将所有珠子顺时针旋转kkk个。F:F:F:将所有珠子以111向下翻…

LeetCode常用算法模式大厂面试题整理

转载自 LeetCode常用算法模式&大厂面试题整理 文章目录 1、滑动窗口 2、双指针 3、快慢指针 4、合并区间 5、循环排序 6、就地反转链表 7、堆-优先队列问题 8、Top K 9、归并 10、单调栈 11、回溯法 BATJ等大厂面试真题汇总 1、滑动窗口 1 一个左指针,一个右…

ABPZero系列教程之拼多多卖家工具

此系列文章围绕着拼多多卖家工具来介绍ABPZero的使用,内容包括手机登录、手机注册、拼团提醒、微信公众号绑定帐号、有拼团发送消息到微信公众号(只要关注过微信公众号并已绑定系统帐号)。学习此系列必备:手机验证码:使…