共计 5946 个字符,预计需要花费 15 分钟才能阅读完成。
如何更改 datax 以反对 hive 的 DECIMAL 数据类型?
1. JAVA 数据类型 – float/double 与 BigDecimal
大家晓得,JAVA 中能够用来存储小数的数字类型,次要包含:
- 根本数据类型 float/double;
- 根本数据类型的对应包装类 java.lang.Float/java.lang.Double;
- java.math 包下的类 java.math.BigDecimal
一般来讲,在不须要齐全准确的计算结果的场景下,能够间接应用 float 或 double 数据类型,其运算效率更高;而在须要准确计算结果的场景下,则必须应用 BigDecimal 类,比方金融场景下波及金额的计算,就必须是齐全准确的计算:
- Decimal data types store exact representations of numeric values, while DOUBLE data types store very close approximations of numeric values.
- Decimal types are needed for use cases in which the (very close) approximation of a DOUBLE is insufficient, such as financial applications, equality and inequality checks, and rounding operations.
- Decimal types are also needed for use cases that deal with numbers outside the DOUBLE range.
在应用 BigDecimal 时,以下细节须要留神:
- BigDecimal 有多个构造方法,其中参数类型为 double 的构造方法的精度不能保障,其后果有肯定的不可预知性,所以不举荐应用参数类型为 double 的构造方法(数值 0.1 无奈精确地示意为 double 数据类型);
- BigDecimal 有多个构造方法,如果心愿 BigDecimal 可能准确地示意冀望的数值,那么肯定要应用参数类型为 String 的构造方法,如 BigDecimal a = new BigDecimal(“0.1”) ;
- 如果不是很在乎是否齐全准确地示意,并应用了 BigDecimal(double),那么要留神 double 自身的特例,double 的标准自身定义了几个非凡的 double 值 (Infinite,-Infinite,NaN),不要把这些值传给 BigDecimal,否则会抛出异样;
-
当必须将 double 用作 BigDecimal 的数据起源时,倡议先应用 Double.toString(double) 将 double 数值转换为 String,而后应用 参数类型为 String 的构造方法;
BigDecimal a = new BigDecimal(double d); // 不举荐应用参数类型为 double 的构造方法,精度不能保障;BigDecimal b= new BigDecimal(String s); // 举荐应用参数类型为 String 的构造方法 static BigDecimal valueOf(double d); // 也能够应用 valueOf 静态方法 DecimalFormat df = new DecimalFormat("#.##");// 能够应用 java.text.DecimalFormat 进行格式化,如这里保留 2 为小数 df.setRoundingMode(RoundingMode.HALF_UP);// 能够指定具体的的舍入模式枚举类 java.math.RoundingMode,默认五舍六入,如这里指定四舍五入;
2. hive 数据类型 – Double,DECIMAL,Numeric
hive 反对以下数字类数据类型,其中 FLOAT 和 DOUBLE 数据类型很早就反对了,而 Decimal 和 NUMERIC 数据类型是后续退出的:
对于 hive 的 Decimal 数据类型,有以下细节须要留神:
- Decimal 数据类型是在 Hive 0.11.0 (HIVE-2693) 引入的,其后在 Hive 0.13.0 (HIVE-3976) 及进行了批改重构;
- 在 Hive 0.13 之前版本,decimal 数据类型的数据精度是固定的 38 位;
- 在 Hive 0.13 及之后版本,申明应用 DECIMAL 数据类型有三种格局:DECIMAL/DECIMAL(precision)/DECIMAL(precision, scale), 用户能够应用第三种格局 DECIMAL(precision, scale) 指定 decimal 的数据精度和数据刻度(小数位数);
- 在 Hive 0.13 及之后版本,若没有指定 DECIMAL 的精度 precision,则默认精度是 10(精度的最大值是 38),若没有指定小数位数 scale,则默认小数位数是 0;
-
Hive 的 Decimal 数据类型底层基于 Java 的 BigDecimal,反对迷信计数法和非迷信计数法:
- The DECIMAL type in Hive is based on Java’s BigDecimal which is used for representing immutable arbitrary precision decimal numbers in Java.
- All regular number operations (e.g. +, -, *, /) and relevant UDFs (e.g. Floor, Ceil, Round, and many more) handle decimal types.
- You can cast to/from decimal types like you would do with other numeric types.
- The persistence format of the decimal type supports both scientific and non-scientific notation. Therefore, regardless of whether your dataset contains data like 4.004E+3 (scientific notation) or 4004 (non-scientific notation) or a combination of both, DECIMAL can be used for it.
-
Double 数据类型提供了数据的近似示意与近似计算,而 Decimal 数据类型提供了数据的准确示意与准确计算,且反对的数据范畴更大:
- Decimal literals provide precise values and greater range for floating point numbers than the DOUBLE type.
- Decimal data types store exact representations of numeric values, while DOUBLE data types store very close approximations of numeric values;
- Decimal types are needed for use cases in which the (very close) approximation of a DOUBLE is insufficient, such as financial applications, equality and inequality checks, and rounding operations.
- They are also needed for use cases that deal with numbers outside the DOUBLE range.
SQL 语句显示转换 decimal 数据类型示例如下:
select CAST(18446744073709001000BD AS DECIMAL(38,0));
select cast(4.004E+3 AS DECIMAL(38,0));
select cast(4004 AS DECIMAL(38,0));
3. 如何更改 datax 以反对 hive 的 DECIMAL 数据类型?
- 如上文所述,在须要对数据进行准确示意和准确运算的场景下(比方金融行业对数据准确性广泛要求较高),咱们须要应用 HIVE 的 Decimal 数据类型而不是 Double 数据类型;
- 然而一些数据同步工具并不间接反对 hive 的 decimal 数据类型,此时须要基于开源版本在外部做二次开发和加强,以反对反对 hive 的 decimal 数据类型;
- 比方开源的 datax 即不反对 hive 的 decimal 数据类型:
那么怎么批改开源的 datax 源码,以加强反对 hive 的 decimal 数据类型呢?
- 因为 datax 的插件化的机制,相干批改只波及到 hdfsreader 和 hdfswriter 插件模块;
-
因为 datax 在读写 hive orc 表时,底层应用的是 hive/orc/hadoop 原生的各种 api, 而这些 hive/orc/hadoop 原生的 api 曾经反对了对 hive Decimal 数据类型的读写操作,所以 datax 的 hdfs reader 和 hdfs writer 插件模块所需的代码批改并不多;
-- datax 读写 hive orc 表时,底层应用的 hive/orc/hadoop 原生 api 局部列表如下:org.apache.hadoop.hive.ql.io.orc.OrcFile; org.apache.hadoop.hive.ql.io.orc.OrcInputFormat; org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat; org.apache.hadoop.hive.ql.io.orc.OrcSerde; org.apache.hadoop.hive.ql.io.orc.Reader; org.apache.hadoop.mapred.RecordWriter; org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector; -- hive 读写 orc 文件数据时,底层调用的是 Core ORC API,这些 api 局部列表如下:org.apache.hadoop.hive.ql.io.orc.OrcFile; org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; org.apache.hadoop.hive.ql.exec.vector.ColumnVector; org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector; org.apache.hadoop.hive.ql.io.orc.Reader; org.apache.hadoop.hive.ql.io.orc.RecordReader; org.apache.hadoop.hive.ql.io.orc.Writer; org.apache.hadoop.hive.ql.io.orc.WriterImpl; org.apache.hadoop.hive.serde2.io.HiveDecimalWritable;
3.1 hdfs reader 相干批改
为反对读取 hive orc 文件中的 decimal 字段,
须要批改 hdfs reader 插件模块中的相干类和办法,这些类和办法次要是
com.alibaba.datax.plugin.reader.hdfsreader.DFSUtil#transportOneRecord
在不是很谨严的情况下,甚至能够不批改 hdfs reader 的任何代码,间接读取 hive orc 表数据并进行数据同步:此时能够在配置同步作业时将 hive 的 Decimal 字段指定为 Double 字段,此时 hdfsreader 在底层调用 hive 的相干 api 读取底层 orc 文件中的相干字段时,会将 DECIMAL 字段值隐式转换为用户指定的 DOUBLE 类型,这种转换在对数据精确性要求不高且数据范畴不大时个别也没有什么大问题(当然思考到数据精确性和数据范畴,仍倡议批改相干办法和类)
3.1 hdfs writer 相干批改
为反对写入数据到 hive orc 文件中的 decimal 字段,
须要批改 hdfs writer 插件模块中的相干类和办法,这些类和办法次要是:
com.alibaba.datax.plugin.writer.hdfswriter.SupportHiveDataType;
com.alibaba.datax.plugin.writer.hdfswriter.HdfsHelper#getColumnTypeInspectors;
com.alibaba.datax.plugin.writer.hdfswriter.HdfsHelper#transportOneRecord(com.alibaba.datax.common.element.Record, java.util.List<com.alibaba.datax.common.util.Configuration>, com.alibaba.datax.common.plugin.TaskPluginCollector);
留神:
- datax 有本人的数据类型,比方 long/Double/String/Date/Boolean/Bytes 等:
- datax 的 Double 类型, 其对应的类不是 java.lang.Double, 而是 com.alibaba.datax.common.element.DoubleColumn,该类外部有 rawData 字段用来存储数据的原始内容,能够存储的数据范畴包含 java.math.BigDecimal,也包含 java.lang.Doulbe;
- datax 的 Double 类型, 在外部会应用办法 com.alibaba.datax.common.element.DoubleColumn#asBigDecimal, 基于 rawData 将字段转换为 java.math.bigDecimal 等, 所以并不会损失 java.math.BigDecimal 的数据精度;
- 为反对 hive orc 中的 Decimal 数据类型,能够在 datax 代码中,通过 hive 的 api
org.apache.hadoop.hive.serde2.typeinfo.HiveDecimalUtils#enforcePrecisionScale() 和 org.apache.hadoop.hive.common.type.HiveDecimal#enforcePrecisionScale,将数据转换为冀望的精度和标度 DECIMAL(precision, scale).