打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
记解决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
public class TestReadFromHive {
public static void main(String[] args) {
hql = "select address from user_info_all limit 10";
Connection con = null;
Statement stmt = null;
ResultSet rs = null;
try {
con = getHiveConnection();
stmt = con.createStatement();
rs = stmt.executeQuery(hql);
Writer out =new BufferedWriter(new OutputStreamWriter(new FileOutputStream("/tmp/address.txt")));
while (rs.next()) {
String address = rs.getString("address");
out.write(address+"\n");
System.out.println(address);
}
out.flush();
out.close();
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
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
export CLASSPATH
$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=testHive org.javali.mr.test.TestReadFromHive

执行脚本后,控制台显示乱码,文件内容也乱码,将文件拷贝到本地($sz file)用不同的编辑器打开依旧乱码。
到这一步可以确定 jdbc读取出来的内容已经是乱码了,由于我的服务器系统编码是en_US , 很有可能是hive jdbc驱动没使用utf-8编码,而获取了系统编码进行转码。为了验证这个假设,我在执行脚本添加了export LANG=zh_CN.UTF-8 ,再次执行,中文显示正常了。

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

同事在apache 的jira 列表里恰好也有找了这个问题: https://issues.apache.org/jira/browse/HIVE-2137

1
2
3
4
5
6
7
8
9
10
11
12
附件的patch里有版本的对比
Index: jdbc/src/java/org/apache/hadoop/hive/jdbc/<span style="color: #ff0000;">HiveQueryResultSet.java</span>
===================================================================
--- jdbc/src/java/org/apache/hadoop/hive/jdbc/HiveQueryResultSet.java (revision 1195103)
+++ jdbc/src/java/org/apache/hadoop/hive/jdbc/HiveQueryResultSet.java (working copy)
@@ -153,7 +153,7 @@
StructObjectInspector soi = (StructObjectInspector) serde.getObjectInspector();
List fieldRefs = soi.getAllStructFieldRefs();
  - Object data = serde.deserialize(new BytesWritable(rowStr.getBytes()));
  + Object data = serde.deserialize(new BytesWritable(rowStr.getBytes("UTF-8")));
assert row.size() == fieldRefs.size() : row.size() + ", " + fieldRefs.size();
for (int i = 0; i < fieldRefs.size(); i++) {

rowStr其实就是hive文件的一行数据,包装成ByteWritable没有指定编码,程序默认会使用系统的编码(en_US),这就是造成乱码的根源。
我们可以根据patch的变更来手动指定编码类型,不过这就得重新编译jdbc驱动了。

  1. 先获取源码,有两种方式:
    1)从apache 的仓库checkout到本地 : http://svn.apache.org/repos/asf/hive/trunk
    2)cd $HIVE_HOME/src下同样可以获取源码
  2. 新建java project——hive_jdbc,然后new package org.apache.hadoop.hive
  3. 从$HIVE_HOME/lib 目录找到以下jar包引入到工程CLASSPATH(hive-jdbc××.jar不需要,我们需要重新编译它)
  4. 把hive的jdbc包下源码拷贝到相应的package,有错误不用管,只要保证HiveQueryResultSet.java类能正常编译,定位到该类需要变更的位置将Object data = serde.deserialize(new BytesWritable(rowStr.getBytes()));替换为Object data = serde.deserialize(new BytesWritable(rowStr.getBytes(“UTF-8″)));
  5. 把编译好的HiveQueryResultSet.class 替换掉hive-jdbc***.jar包里的旧类,同时为了区分,把包重命名为hive-jdbc-0.7.0-CDH3B4_UTF8.jar
  6. 上传到服务器上,替换原来的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-jdbc-0.7.0-CDH3B4_UTF8 下载后直接将扩展名改为jar即可

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Apache Sentry手动安装、使用手册
sqoop 安装及测试
解决(Spark)openfire在使用MySQL数据库后的中文乱码问题
Hive:用Java代码通过JDBC连接Hiveserver
Hadoop Hive与Hbase整合+thrift
Java丨Cmd运行Jar出现乱码问题
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服