Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 783|回复: 0

Hive中文注释乱码解决方案

[复制链接]
  • TA的每日心情
    奋斗
    3 小时前
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-5-30 17:26:50 | 显示全部楼层 |阅读模式

    本文来自网易云社区

    作者:王潘安


    快速解决方法

    目前的hive客户端在执行desc tablexxx和show create table xxx命令的时候,字段的中文注释会出现乱码情况,如(????)。在使用 ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe' 建表的时候,注释则会出现from deserializer。以下几个步骤可以帮你快速解决这些问题:

    1.首先在hive客户端的conf目录下找到hive-site.xml配置文件,查询本机hive所连接的metastore地址。例如:

            <property>
                    <name>javax.jdo.option.ConnectionURL</name>
                    <value>jdbc:mysql://10.120.xxx.xxx:3306/hive_study</value>
            </property>

    连接该数据库。找到表COLUMNS_V2并查询其中的字段。如果为类似以下的乱码:

     |   952 | ??????????                                                                                                                                                                                      | in_l_notice_mail             | string             |          14 |
    |   952 | ????????                                                                                                                                                                                        | live_course_notice_mail      | string             |          22 |
    |   952 | ????????????????                                                                                                                                                                                | mark_best_reply              | string             |           4 |
    |   952 | ???????????                                                                                                                                                                                     | platform_notice_mail         | string             |          13 |
    |   952 | ????????                                                                                                                                                                                        | question_answered            | string             |           3 |
    |   952 | ?????????                                                                                                                                                                                       | question_digged              | string             |           6 |
    |   952 | sns???????                                                                                                                                                                                      | sns_friends_attend           | string             |          20 |
    |   952 | sns???????                                                                                                                                                                                      | sns_new_fans                 | string             |          21 |
    |   952 |                                                                                                                                                                                                 | study_plan_notice            | string             |          15 |
    |   952 | ??id

    则对数据库做如下设置:

      //修改字段注释字符集
    alter table COLUMNS_V2 modify column COMMENT varchar(256) character set utf8;
    //修改表注释字符集
    alter table TABLE_PARAMS modify column PARAM_VALUE varchar(4000) character set utf8;
    //修改分区注释字符集
    alter table PARTITION_KEYS modify column PKEY_COMMENT varchar(4000) character set utf8;

    并且重新建hive表(不是metastore的表)。如果COLUMNS_V2中的数据不为乱码,则不用做任何操作,跳过此步。

    2.将本文附件中的两个jar包替换到hive客户端的lib目录中。

    3.重启hive客户端,测试一下效果。

    解决过程及BUG原因解释

    1.准备工作

    首先在自己电脑上部署HADOOP,HIVE环境。下载hive和Hive-JSON-Serde,链接:https://github.com/apache/hive https://github.com/rcongiu/Hive-JSON-Serde 确保可以运行起来。然后就要做好以下准备:

    1.找到hive运行时打印的log的位置。如果没有配置如下属性,那就是默认位置:

           <property>
        <name>hive.exec.local.scratchdir</name>
        <value>${system:java.io.tmpdir}/${system:user.name}</value>
        <description>Local scratch space for Hive jobs</description>
      </property>

    以云课堂为例,就是放在/tmp/study目录下,日志会记录在hive.log中。

    2.准备好调试环境。找到hadoop的目录,在bin目录下找到hadoop这个可执行的命令,打开它,找到如下一段代码:

          if [ "$COMMAND" = "fs" ] ; then
         .apache.hadoop.fs.FsShell
        elif [ "$COMMAND" = "version" ] ; then
         .apache.hadoop.util.VersionInfo
        elif [ "$COMMAND" = "jar" ] ; then
         .apache.hadoop.util.RunJar
        elif [ "$COMMAND" = "key" ] ; then
         .apache.hadoop.crypto.key.KeyShell
        elif [ "$COMMAND" = "checknative" ] ; then
         .apache.hadoop.util.NativeLibraryChecker

    把他改为:

        if [ "$COMMAND" = "fs" ] ; then
         .apache.hadoop.fs.FsShell
        elif [ "$COMMAND" = "version" ] ; then
         .apache.hadoop.util.VersionInfo
        elif [ "$COMMAND" = "jar" ] ; then
          if ! echo "$HADOOP_CLIENT_OPTS"|fgrep 'dt_socket' ; then
            HADOOP_CLIENT_OPTS="$HADOOP_CLIENT_OPTS  -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8765"
          fi
          HIVE_DEBUG_RECURSIVE=y
          HIVE_MAIN_CLIENT_DEBUG_OPTS="suspend=n,address=8765"
          HIVE_CHILD_CLIENT_DEBUG_OPTS="suspend=y,address=8766"
          export HADOOP_CLIENT_OPTS HIVE_CHILD_CLIENT_DEBUG_OPTS HIVE_DEBUG_RECURSIVE HIVE_MAIN_CLIENT_DEBUG_OPTS
         .apache.hadoop.util.RunJar
        elif [ "$COMMAND" = "key" ] ; then
         .apache.hadoop.crypto.key.KeyShell
        elif [ "$COMMAND" = "checknative" ] ; then
         .apache.hadoop.util.NativeLibraryChecker

    这样,就打开了端口号为8765的远程调试端口

    具体为啥这样设置,参考这篇博客:http://blog.csdn.net/wisgood/article/details/38047921

    2.查找BUG

      首先声明,以下所有的调试都是基于hive 1.2.1版本和json-serde 1.3.6的版本。

      Hive客户端的启动是在源码的cli目录下的org.apache.hadoop.hive.cli包中的CliDriver类中,它又具体去调用了ql目录下的org.apache.hadoop.hive.ql包中的Driver类。因此,我们在IDE中只需要引入ql目录下的maven工程即可。

      打开Driver类,找到它的run方法,这便是hive命令执行的函数入口。在run方法中,主要调用的就是runInternal方法。而runInternal方法中主要就是调用了compileInternal和execute方法,也就是编译命令和执行命令两部分。

    编译阶段

      首先我们看看compile阶段干了些啥,通过跟踪compileInternel方法,我们可以知道,它最终调用的是compile方法。这个方法主要是用来生成执行计划树的。首先解析出树形结构,这段代码慎重跟踪下去,水太深,会游泳的可以下去看看:

           ParseDriver pd = new ParseDriver();
          ASTNode tree = pd.parse(command, ctx);

    然后进行语义分析:

     BaseSemanticAnalyzer sem = SemanticAnalyzerFactory.get(conf, tree);
     sem.analyze(tree, ctx);

    接着生成执行计划:

           plan = new QueryPlan(queryStr, sem, perfLogger.getStartTime(PerfLogger.DRIVER_RUN), queryId,
              SessionState.get().getCommandType());

    最后还要拿到返回结果的模板,也就是说明返回值的样子:

     schema = getSchema(sem, conf);

    在这个过程中,我们重点关注的是语义分析和返回值模板的情况。可以看到,语义分析首先通过工厂返回了一个BaseSemanticAnalyzer,这是一个父类,而我们执行类似show create table xxx的时候,它实际执行的是它的一个子类DDLSemanticAnalyzer中的analyzeInternal方法。我们去看这个方法,发现它是一堆switch case,我们在其中找到如下代码:

         case HiveParser.TOK_SHOW_CREATETABLE:     
          ctx.setResFile(ctx.getLocalTmpPath());
          analyzeShowCreateTable(ast);
          break;

    跟到analyzeShowCreateTable方法中去,发现有这样一段代码:

     Table tab = getTable(tableName);

    好激动,貌似拿到了表信息,我们把断点打在这,并且进行调试,看看这个tab变量里面究竟有啥: 
    发现什么表信息都有,的确去metastore库里面拿了东西,注意:我这里的comment是???这样的乱码是因为我的metastore里面的表的编码格式没改。所以到目前为止,hive的代码是正常的。

    在analyzeShowCreateTable方法中,还有几个要注意的点,一个是:

     showCreateTblDesc = new ShowCreateTableDesc(tableName, ctx.getResFile().toString());

    一个是:

     setFetchTask(createFetchTask(showCreateTblDesc.getSchema()));

    还有一个:

         rootTasks.add(TaskFactory.get(new DDLWork(getInputs(), getOutputs(), showCreateTblDesc), conf));

    首先看ShowCreateTableDesc类里面有什么,发现有这样一个字符串常量:

     private static final String schema = "createtab_stmt#string";

    通过对比这个类同包中的其他类的schema常量,比如DescTableDesc类:

    private static final String schema = "col_name,data_type,comment#string:string:string";

    不难猜出,这个就是定义输出结果的样子和每个结构的类型的常量。接着我们看createFetchTask方法,在这个里面,它把上面说的字符串schema解析出来放到了Properties配置中:

        String[] colTypes = schema.split("#");
        prop.setProperty("columns", colTypes[0]);
        prop.setProperty("columns.types", colTypes[1]);
        prop.setProperty(serdeConstants.SERIALIZATION_LIB, LazySimpleSerDe.class.getName());

    看到这段代码,简直点燃了我的兴奋点!我们的问题不就是字段注释乱码吗?(剧透一下,如果你不给hive传注释信息,那么hive就给你写个from deserializer)这里定义了字段名字,字段类型以及序列化类,但是没有定义字段注释!难道问题就出在这?然而仔细想想,这个地方就是误导你的。因为在这个阶段虽然已经在metastore中获取了table信息,然而这个阶段仍然是编译阶段,具体的命令还没有执行。另外在property中,设置的都是常量,所以它更不可能是最后的返回结果。其实它的作用是描述这个模板的类型以及序列化方法,而不是用来描述最后的结果的。

    最后我们再来看rootTasks.add方法干了什么,可以发现,TaskFactory通过get方法产生了一个task,放到了task的序列中,这个get方法通过你传入进来的work类型,找到相应的task类型,并且通过java的反射机制创建一个task实例。在这里,我们传入的是一个DDLWork,因此它会创建一个DDLTask。我们要牢记这个DDLTask类,因为后面的执行过程中,就是去执行它!


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

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




    相关文章:
    【推荐】 医疗大数据的分析和挖掘发展现状以及未来的应用前景
    【推荐】 使用 Prometheus + Grafana 对 Kubernetes 进行性能监控的实践
    【推荐】 用scrapy数据抓取实践

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-11-24 19:19 , Processed in 1.117254 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表