并行读Oracle数据结果不匹配问题

27 阅读2分钟

并行读Oracle数据结果不匹配问题

最近有用户抱怨,用我们的服务并行读oracle数据并写到hive, 虽然count前后匹配,但是求和前后不匹配的问题,用户数据量大概260万左右,并行度设置为10,当时第一是oracle或者hive某一方对数据进行了四舍五入,导致求和有出入,当然还是要验证一下。话不多说先在本地复现用户的问题 源数据count

image.png 源数据对某一业务字段求和

image.png

目标数据count

268822

目标数据对某一业务字段求和

-4578645623.89

求和的值差了很多,排除四舍五入这个方向。 为了复现这个问题,我打算缩小下读的数据量,怎么缩小呢,oracle里一般都用rownum来取前n行数据,类似于MySQL中的limit, 于是我用1万,5万,10万,130万,做测试,却怎么也无法复现问题。这个方向行不通,就去看一下实现并行读oracle的代码,发现

query = "SEELCT MOD(ROWNUM, " + parallelNum + " ) AS PARTITION_COLUMN";

当前代码的逻辑是对ROWNUM取余得到oracle并行读的分区column,这就是问题所在,因为ROWNUM是一个虚拟的列,具有不确定性,尽管spark没有对并行读oracle的partitioncolumn做过多的限制,但其实这个列要求是一个确定的列,最好是主键或者唯一键。而当前用户的业务表没有主键或者唯一键,鉴于此,我建议用户串行加载这批数据,然而用户测试之后,新的问题又来了,她们抱怨读260万的oracle数据到hive居然用了两个小时。 于是我接着调研,首先看下用户读Oracle的时候是否设置了fetchSize,检查了一下用户的配置文件,果然没设置,建议用户配置fetchsize=5000,执行时间降为40分钟。 进一步调优,由于串行读数据,spark在写hive的时候默认也是只有一个分区,建议用户配置repartition参数为10,可以并行写hive,执行时间降为25分钟。 还是有些慢,用户还是不接受,继续调研Oracle并行读, 既然ROWNUM是虚拟键,不能做读oracle的partitionColumn,那我们就想办法找一个非虚拟键,确定的键,查阅oracle官网文档后发现,有一个宝藏列rowid, ROWID数据库中行的全局唯一地址, 因此可以用ROWID来构造oracle的PartitionColumn,简单的思路就是可以用MOD(HASH(ROWID, parallelNum)的方式。 测试,执行时间降为8分钟,回归测试通过问题解决。

//TODO:ROW_NUMER的方式