zoukankan      html  css  js  c++  java
  • Hive中文注释乱码解决方案(2)

    本文来自网易云社区

    作者:王潘安


    执行阶段

    launchTask    回到Driver类的runInternal方法,看以下执行过程。在runInternal方法中,执行过程调用了execute方法。execute方法里面的内容很多,但是跟我们有关系的就只有launchTask方法。这个方法里面有这么关键的几步:

         tsk.initialize(conf, plan, cxt);
        TaskResult tskRes = new TaskResult();
        TaskRunner tskRun = new TaskRunner(tsk, tskRes);
    
        cxt.launching(tskRun);
    
        tskRun.runSequential();

    跟进runSequential方法发现调用了如下方法:

     exitVal = tsk.executeTask();

    接着跟进,发现执行了这段代码:

         int retval = execute(driverContext);

    这个execute 方法在执行show create table xx命令时就是执行的DDLTask类中的execute方法。

    跟进execute方法找到如下代码:

           ShowCreateTableDesc showCreateTbl = work.getShowCreateTblDesc();
          if (showCreateTbl != null) {
            return showCreateTable(db, showCreateTbl);
          }

    查看showCreateTable方法,发现它干的就是把返回结果的字段都拼接成模板,然后把从metastore里面拿到的内容塞进去,最后写到一个临时文件流里面。我们发现,它最后是这样写到文件流的:

     outStream.writeBytes(createTab_stmt.render());

    中文在这个地方估计被写成乱码了,于是把它改为:

         outStream.write(createTab_stmt.render().getBytes("UTF-8"));

    重新编译一下hive:

     mvn clean package -Phadoop-2 -DskipTests

    把编译完成后的hive源码的ql/target目录的hive-exec-1.2.1.jar替换到运行的hive的lib目录中,建一个测试表,不用json序列化反序列化,发现show create table xx命令的字段中文注释正常了。但是如果测试表仍用json序列化和反序列化,那么仍然会出现注释为from deserializer的现象。

    我们回到代码,看看在showCreateTable方法中究竟是如何获取字段的注释信息的。找到如下这段代码:

         List<FieldSchema> cols = tbl.getCols();

    跟进去发现,如果设置了自定义的序列化与反序列化类,就会执行这行操作:

     return MetaStoreUtils.getFieldsFromDeserializer(getTableName(), getDeserializer());

    跟进getFieldsFromDeserializer方法,我们发现如下几行重要代码:

     ObjectInspector oi = deserializer.getObjectInspector();
      List<? extends StructField> fields = ((StructObjectInspector) oi).getAllStructFieldRefs();
      for (int i = 0; i < fields.size(); i++) {
        StructField structField = fields.get(i);
        String fieldName = structField.getFieldName();
        String fieldTypeName = structField.getFieldObjectInspector().getTypeName();
        String fieldComment = determineFieldComment(structField.getFieldComment());
    
        str_fields.add(new FieldSchema(fieldName, fieldTypeName, fieldComment));
      }

    也就是说注释是从deserializer中拿出来的。那我们在返回去看看,hive给我们的json deserializer传了什么参数。返回到上一段代码,我们看getDeserializer方法干了什么:

         deserializer = getDeserializerFromMetaStore(false);

    我们最好在这打个断点,看看,跟进代码发现执行了:

         return MetaStoreUtils.getDeserializer(SessionState.getSessionConf(), tTable, skipConfError);

    然后通过反射建了一个Deserializer的实例,并且调用了它的initialize方法:

           Deserializer deserializer = ReflectionUtil.newInstance(conf.getClassByName(lib).
                  asSubclass(Deserializer.class), conf);
           SerDeUtils.initializeSerDeWithoutErrorCheck(deserializer, conf,
                    MetaStoreUtils.getTableMetadata(table), null);

    在跟进initializeSerDeWithoutErrorCheck方法,发现它执行了:

     deserializer.initialize(conf, createOverlayedProperties(tblProps, partProps));

    我们在跟进以下MetaStoreUtils.getTableMetadata(table)发现它执行了MetaStoreUtils.getSchema这个方法。跟进去,我们发现了至关重要的代码,注意所有的奥妙都在这:

         for (FieldSchema col : tblsd.getCols()) {
          if (!first) {
            colNameBuf.append(",");
            colTypeBuf.append(":");
            colComment.append('');
          }
          colNameBuf.append(col.getName());
          colTypeBuf.append(col.getType());
          colComment.append((null != col.getComment()) ? col.getComment() : "");
          first = false;
        }
        String colNames = colNameBuf.toString();
        String colTypes = colTypeBuf.toString();
        schema.setProperty(
            org.apache.hadoop.hive.metastore.api.hive_metastoreConstants.META_TABLE_COLUMNS,
            colNames);
        schema.setProperty(
            org.apache.hadoop.hive.metastore.api.hive_metastoreConstants.META_TABLE_COLUMN_TYPES,
            colTypes);
        schema.setProperty("columns.comments", colComment.toString());

    也就是说,Hive是给序列化反序列化类传了注释的信息,首先注释的信息是以分割的,其次它是放在key值为columns.comments的property中。


    Hive-JSON-Serde调试

    然后我们就要打开Hive-JSON-Serde看看它怎么处理这些信息的。很容易找到类JsonSerDe。看看它的initialize方法:

             String columnNameProperty = tbl.getProperty(Constants.LIST_COLUMNS);
            String columnTypeProperty = tbl.getProperty(Constants.LIST_COLUMN_TYPES);

    它根本就没有拿注释信息!然后看它怎么生成的rowObjectInspector:

     rowObjectInspector = (StructObjectInspector) JsonObjectInspectorFactory
                    .getJsonObjectInspectorFromTypeInfo(rowTypeInfo, options);

    跟入getJsonObjectInspectorFromTypeInfo方法,找到:

                         result = JsonObjectInspectorFactory.getJsonStructObjectInspector(fieldNames,
                                fieldObjectInspectors, options);

    接着跟进去,发现:

                 result = new JsonStructObjectInspector(structFieldNames,
                        structFieldObjectInspectors, options);

    我们看看这个JsonStructObjectInspector类,它是继承的StandardStructObjectInspector,它的构造函数调用了父类的:

       protected StandardStructObjectInspector(List<String> structFieldNames,
          List<ObjectInspector> structFieldObjectInspectors) {
        init(structFieldNames, structFieldObjectInspectors, null);
      }

    一看init函数最后传入的参数是null,就知道问题出在这了,这个父类其实还有另外一个构造方法:

       protected StandardStructObjectInspector(List<String> structFieldNames,
          List<ObjectInspector> structFieldObjectInspectors,
          List<String> structFieldComments) {
        init(structFieldNames, structFieldObjectInspectors, structFieldComments);
      }

    也就是说,它是允许传入注释信息的。那么我们的思路就明确了,第一,把未解析出来的注释信息解析出来。第二,把这个注释信息传入JsonStructObjectInspector的构造函数中:

     String columnCommentProperty = tbl.getProperty("columns.comments");
     if (columnCommentProperty != null){
            if (columnCommentProperty.length() == 0) {
                columnComments = new ArrayList<String>();
            } else {
                columnComments = Arrays.asList(columnCommentProperty.split("", columnNames.size()));
            }
     }

    这里有一点要注意:在StandardStructObjectInspector类中,它会强制检查字段数与注释数相等,所以在做split操作时,一定要传2个参数,把注释为空的字段补全,否则要出bug。后面的操作就是把这个注释传参到各个函数中去,这里就不在多述。

    然后把JsonStructObjectInspector的构造函数改为:

           public JsonStructObjectInspector(List<String> structFieldNames,
                List<ObjectInspector> structFieldObjectInspectors, List<String> structFieldComments, JsonStructOIOptions opts) {
            super(structFieldNames, structFieldObjectInspectors, structFieldComments);
            
            options = opts;
        }

    最后,重新编译Hive-JSON-Serde:

     mvn -Phdp23 clean package

    这段代码改动比较多。


    3.总结

    其实Hive中文注释乱码就两个原因造成的。一个是Hive在写注释到流中时,没有把编码格式转为UTF-8。在第三方插件Hive-JSON-Serde中,没有将注释保存下来,如果注释为空,Hive会自动补上from deserializer的字符串。

      因此只需要改动下面一小点即可,首先在Hive的源码中,找到ql目录,找到org.apache.hadoop.hive.ql.exec中的DDLTask类,找到showCreateTable方法。修改第2110行的代码:

     outStream.writeBytes(createTab_stmt.render());

    为:

         outStream.write(createTab_stmt.render().getBytes("UTF-8"));

      对于Hive-JSON-Serde来说,则是在它的JsonSerDe类的initialize方法中加入解析字段注释的代码:

      String columnCommentProperty = tbl.getProperty("columns.comments");
     if (columnCommentProperty != null){
            if (columnCommentProperty.length() == 0) {
                columnComments = new ArrayList<String>();
            } else {
                columnComments = Arrays.asList(columnCommentProperty.split("", columnNames.size()));
            }
     }

    并且在构建rowObjectInspector的时候将注释信息传入:

             rowObjectInspector = (StructObjectInspector) JsonObjectInspectorFactory
                    .getJsonObjectInspectorFromTypeInfo(rowTypeInfo, columnComments, options);

    然后把JsonStructObjectInspector的构造函数改为:

            public JsonStructObjectInspector(List<String> structFieldNames,
                List<ObjectInspector> structFieldObjectInspectors, List<String> structFieldComments, JsonStructOIOptions opts) {
            super(structFieldNames, structFieldObjectInspectors, structFieldComments);
            
            options = opts;
        }



    相关阅读:Hive中文注释乱码解决方案(1)


    网易云免费体验馆,0成本体验20+款云产品!

    更多网易研发、产品、运营经验分享请访问网易云社区


    相关文章:
    【推荐】 wireshark抓包分析——TCP/IP协议

  • 相关阅读:
    A* Pathfinding for Beginners
    OpenGL Draw Mesh
    CentOS6.5下安装、配置SSH
    Ubuntu18.04下搭建LAMP环境
    滚动合集
    关闭页面触发事件
    在table中tr的display:block在firefox下显示布局错乱问题
    sharepoint添加模板及删除模板
    常用正则表达式
    javascript对象的property和prototype是这样一种关系
  • 原文地址:https://www.cnblogs.com/zyfd/p/9797169.html
Copyright © 2011-2022 走看看