hive mysql 乱码_记解决Hive Jdbc中文乱码

乱码一直就是个很麻烦的事情,项目有个功能需要从hive表获得用户的住址(住址有中文)

jdbc执行(查询select address from user_info_all limit 10)的结果直接插入到mysql表(utf8编码),再查看mysql全是乱码

首先怀疑是hive文件编码有问题,于是用CLI方式查询同一条语句,结果中文显示正常

这说明乱码与hive文件编码没有关系,为了排除SecureCRT编码因素,我写了个测试类——从hive表执行这条语句,输出到控制台的同时也原样输出到文件。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

publicclassTestReadFromHive{

publicstaticvoidmain(String[]args){

hql="select address from user_info_all limit 10";

Connectioncon=null;

Statementstmt=null;

ResultSetrs=null;

try{

con=getHiveConnection();

stmt=con.createStatement();

rs=stmt.executeQuery(hql);

Writerout=newBufferedWriter(newOutputStreamWriter(newFileOutputStream("/tmp/address.txt")));

while(rs.next()){

Stringaddress=rs.getString("address");

out.write(address+"\n");

System.out.println(address);

}

out.flush();

out.close();

}catch(SQLExceptione){

e.printStackTrace();

}catch(Exceptione){

e.printStackTrace();

}

}

编译后上传到服务器上,编写执行脚本:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

#!/bin/bash

MY_LIB=./lib

MY_JAR=\

$MY_LIB/hive-anttasks-0.7.0-CDH3B4.jar:\

$MY_LIB/hive-cli-0.7.0-CDH3B4.jar:\

#$MY_LIB/hive-jdbc-0.7.0-CDH3B4.jar:\

$MY_LIB/hive-jdbc-0.7.0-CDH3B4.jar:\

$MY_LIB/hive-common-0.7.0-CDH3B4.jar:\

$MY_LIB/hive-metastore-0.7.0-CDH3B4.jar:\

$MY_LIB/hive-contrib-0.7.0-CDH3B4.jar:\

$MY_LIB/hive-serde-0.7.0-CDH3B4.jar:\

$MY_LIB/hive-exec-0.7.0-CDH3B4.jar:\

$MY_LIB/hive-service-0.7.0-CDH3B4.jar:\

$MY_LIB/hive-hbase-handler-0.7.0-CDH3B4.jar:\

$MY_LIB/hive-shims-0.7.0-CDH3B4.jar:\

$MY_LIB/hive-hwi-0.7.0-CDH3B4.jar:\

$MY_LIB/libfb303.jar:\

$MY_LIB/hadoop-core-0.20.2-CDH3B4.jar:\

$MY_LIB/commons-logging-1.0.4.jar:\

$MY_LIB/commons-logging-api-1.0.4.jar:\

$MY_LIB/slf4j-api-1.4.3.jar:\

$MY_LIB/slf4j-log4j12-1.4.3.jar:\

$MY_LIB/log4j-1.2.15.jar

CLASSPATH=\

.:\

$MY_JAR

echo$CLASSPATH

exportCLASSPATH

$JAVA_HOME/bin/java-Xms128m-Xmx128m-Xmn32m-XX:+UseParNewGC-XX:+UseConcMarkSweepGC-XX:+UseTLAB-XX:+CMSIncrementalMode-XX:+CMSIncrementalPacing-XX:CMSIncrementalDutyCycleMin=0-XX:CMSIncrementalDutyCycle=10-Dcrgw.module=testHiveorg.javali.mr.test.TestReadFromHive

执行脚本后,控制台显示乱码,文件内容也乱码,将文件拷贝到本地($sz file)用不同的编辑器打开依旧乱码。

到这一步可以确定 jdbc读取出来的内容已经是乱码了,由于我的服务器系统编码是en_US , 很有可能是hive jdbc驱动没使用utf-8编码,而获取了系统编码进行转码。为了验证这个假设,我在执行脚本添加了export LANG=zh_CN.UTF-8 ,再次执行,中文显示正常了。

我们已经找到问题了,有一种解决办法就是将系统编码或者将运行时环境(Tomcat)的编码改为UTF-8,如果是个新开展的项目,做统一的编码设置无可厚非,如果因为要从hive表取一个中文字段,去改变现有系统运行环境的编码,决不是可取的解决办法,因为这个改动很有可能影响其他模块的可用性。

1

2

3

4

5

6

7

8

9

10

11

12

附件的patch里有版本的对比

Index:jdbc/src/java/org/apache/hadoop/hive/jdbc/HiveQueryResultSet.java

===================================================================

---jdbc/src/java/org/apache/hadoop/hive/jdbc/HiveQueryResultSet.java(revision1195103)

+++jdbc/src/java/org/apache/hadoop/hive/jdbc/HiveQueryResultSet.java(workingcopy)

@@-153,7+153,7@@

StructObjectInspectorsoi=(StructObjectInspector)serde.getObjectInspector();

ListfieldRefs=soi.getAllStructFieldRefs();

-Objectdata=serde.deserialize(newBytesWritable(rowStr.getBytes()));

+Objectdata=serde.deserialize(newBytesWritable(rowStr.getBytes("UTF-8")));

assertrow.size()==fieldRefs.size():row.size()+", "+fieldRefs.size();

for(inti=0;i

rowStr其实就是hive文件的一行数据,包装成ByteWritable没有指定编码,程序默认会使用系统的编码(en_US),这就是造成乱码的根源。

我们可以根据patch的变更来手动指定编码类型,不过这就得重新编译jdbc驱动了。

先获取源码,有两种方式:

1)从apache 的仓库checkout到本地 : http://svn.apache.org/repos/asf/hive/trunk

2)cd $HIVE_HOME/src下同样可以获取源码

新建java project——hive_jdbc,然后new package org.apache.hadoop.hive

从$HIVE_HOME/lib 目录找到以下jar包引入到工程CLASSPATH(hive-jdbc××.jar不需要,我们需要重新编译它)

把hive的jdbc包下源码拷贝到相应的package,有错误不用管,只要保证HiveQueryResultSet.java类能正常编译,定位到该类需要变更的位置将Object data = serde.deserialize(new BytesWritable(rowStr.getBytes()));替换为Object data = serde.deserialize(new BytesWritable(rowStr.getBytes(“UTF-8″)));

把编译好的HiveQueryResultSet.class 替换掉hive-jdbc***.jar包里的旧类,同时为了区分,把包重命名为hive-jdbc-0.7.0-CDH3B4_UTF8.jar

上传到服务器上,替换原来的jar包,再次执行测试脚本,此时无论系统编码是什么,只要SecureCRT客户端编码正确,输出到Console或者File都能正常显示中文了不过获取到address字段后并不能正常显示,需要重新用UTF-8构造一遍

String address = rs.getString(“address”); //直接system.out.println 在console会显示乱码

System.out.println(new String(address.getBytes(“UTF-8″)));

这样才能在console显示正常,直接insert到mysql是能正常显示的

借鉴mysql,我们要做的更好

虽然解决了问题,但出现了UTF-8硬编码,这就限定了只适用于UTF-8编码的场景,如果文件编码为GBK,问题又将来临

我们在使用mysql的jdbc连接时,会在jdbc url加上编码设置:

jdbc\:mysql\://localhost\:3306/mdmy?autoReconnect\=true&useUnicode\=true&characterEncoding\=UTF8

同样一种可配置、更通用的方案就是在hive jdbc url上附加上编码参数,将这个参数带到HiveQueryResultSet类使用它转码

这种方案需要对url连接的解析做下变更,但也不算困难,这个留到以后再实现

你可能感兴趣的:(hive,mysql,乱码)