1. 程式人生 > >Hive中文註釋亂碼解決方案

Hive中文註釋亂碼解決方案

本文來自網易雲社群

作者:王潘安


快速解決方法

目前的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變數裡面究竟有啥: tab.png?version=1&modificationDate=1456031285000
發現什麼表資訊都有,的確去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+款雲產品!

更多網易研發、產品、運營經驗分享請訪問網易雲社群




相關文章:
【推薦】 BRVAH(讓RecyclerView變得更高效)(1)