Datax-Hdfs如何实现支持binary类型数据读写

一、问题背景

      主要的源码思路还是按照这篇HdfsWriter如何实现支持decimal类型数据写入来,这里不再介绍。上篇说binary类型读取会有坑,因此这篇主要解释一下坑的地方以及解决的办法。

PS:虽说binary类型的字段读写Hive,本人没有想到对于数据仓库建设使用上有什么用处,但是本着好奇、学习的心态,就去研究了一下并集成到datax内。

开头附上github地址

二. 环境准备

Datax版本:3.0

Hadoop版本:2.7.3

Hive版本:2.3.2

三. 源码

    在之前一篇文章中可以看到,hive底层源码对于binary类型字段是支持的,也提供了给定的class对象。

binaryTypeEntry = new PrimitiveObjectInspectorUtils.PrimitiveTypeEntry(PrimitiveCategory.BINARY, "binary", byte[].class, byte[].class, BytesWritable.class);

   因此我直接就使用了byte[].class作为了hdfs写入的序列化器。

HdfsHelper:

// orc需要获取对应的序列化器
public List<ObjectInspector>  getColumnTypeInspectors(List<Configuration> columns){
...
case BINARY:
   objectInspector = ObjectInspectorFactory.getReflectionObjectInspector(byte[].class, ObjectInspectorFactory.ObjectInspectorOptions.JAVA);
break;
...
}

// 字段从channel中读取转换真正类型
public static MutablePair<List<Object>, Boolean> transportOneRecord(
            Record record,List<Configuration> columnsConfiguration,
            TaskPluginCollector taskPluginCollector){
   String rowData = column.getRawData().toString();
   case BINARY:
     //  这里如果使用 rowData.getBytes() ==> 得到的是 byte数组字符串地址的字节数组
     //  即 "[B@74ad1f1f".getBytes(),所以得使用column.asBytes()
     recordList.add(column.asBytes());
   break;
}

在写入orc类型文件时,在hive中查询没有任何问题

而在text类型的文件查询中时,显示的是byte数组的内存地址

说明在hdfs在写入text文件时存在问题,接下来首先定位问题所在,在接着看如何改造。

HdfsHelper:

// text类型文件最终写入要转换为string类型进行字符串拼接
public static MutablePair<Text, Boolean> transportOneRecord(
            Record record, char fieldDelimiter, List<Configuration> columnsConfiguration, TaskPluginCollector taskPluginCollector) {
...
        if(null != transportResultList){
            // 调用column对象toString进行拼接
            Text recordResult = new Text(StringUtils.join(transportResultList.getLeft(), fieldDelimiter));
            transportResult.setRight(transportResultList.getRight());
            transportResult.setLeft(recordResult);
        }
...
    }

所以很明显的是,byte[]数组的toString方法肯定是取得内存地址。因此在做值转换的时候我们,不能采用直接用byte[]对象。观察到开头hive源码提供的BytesWritable对象,查看该对象,发现这个对象已经重写了toString方法,所以我们就采用这个对象去做类型转换。

public List<ObjectInspector>  getColumnTypeInspectors(List<Configuration> columns){
...
case BINARY:
   objectInspector = ObjectInspectorFactory.getReflectionObjectInspector(BytesWritable.class, ObjectInspectorFactory.ObjectInspectorOptions.JAVA);
break;
...
}



public static MutablePair<List<Object>, Boolean> transportOneRecord(
            Record record,List<Configuration> columnsConfiguration,
            TaskPluginCollector taskPluginCollector){
...
 case BINARY:
     recordList.add(new BytesWritable(column.asBytes()));
 break;
...
}

经过测试,text类型以及orc类型的文件全部写入正常。

      到这里肯定会有人和我有同样想法,那既然我已经写入了,我用datax去做读取数据的时候,重写写入Mysql还和原先的值是否能保持一致。因此直接看了下hdfsreader配置,发现不支持binary,统一用string方法去接数据,最终入库的数据,是一串数字+空格组成的数据。

   因为hdfs读取string时是不做处理的,直接将值拿过来,说明在hdfs里存的值就是这个,但是hive查出来又是正确的,就很神奇,这里就回想起之前BytesWritable对象的toString方法。

BytesWritable:

 /**
   * Generate the stream of bytes as hex pairs separated by ' '.
   */
  @Override
  public String toString() { 
    StringBuilder sb = new StringBuilder(3*size);
    for (int idx = 0; idx < size; idx++) {
      // if not the first, put a blank separator in
      if (idx != 0) {
        sb.append(' ');
      }
      String num = Integer.toHexString(0xff & bytes[idx]);
      // if it is only one digit, add a leading 0.
      if (num.length() < 2) {
        sb.append('0');
      }
      sb.append(num);
    }
    return sb.toString();
  }

      这就能解释为啥是数字加空格了,他将byte转为16进制数,在通过+空格的方式做转换了。看到这,那就马上想到,hdfsreader中我只要加上binary类型,然后重新将这个字符串转为byte数组问题不就解决了么。

orc类型文件需要改DFSUtil类:

private Record transportOneRecord(List<ColumnEntry> columnConfigs, List<Object> recordFields
            , RecordSender recordSender, TaskPluginCollector taskPluginCollector, boolean isReadAllColumns, String nullFormat) {
...
case BINARY:
    String [] array = columnValue.split("\\s");
    byte[] bytes = new byte[array.length];
    for (int i = 0; i<array.length;i++){
        bytes[i] = (byte) Integer.parseInt(array[i], 16);
    }
    columnGenerated = new BytesColumn(bytes);
break;
...
}

text类型文件需要改UnstructuredStorageReaderUtil:

public static Record transportOneRecord(RecordSender recordSender,
											List<ColumnEntry> columnConfigs, String[] sourceLine,
											String nullFormat, TaskPluginCollector taskPluginCollector) {
...
case BINARY:
    String [] array = columnValue.split("\\s");
    byte[] bytes = new byte[array.length];
    for (int i = 0; i<array.length;i++){
        bytes[i] = (byte) Integer.parseInt(array[i], 16);
    }
    columnGenerated = new BytesColumn(bytes);
break;
...
}

接着,在自己的job文件里将binary字段加上,再进行数据从hive同步到Mysql看结果,无论是orc文件还是text类型文件,结果显示都是正常的:

四、代码地址

github地址

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值