前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >0754-5.16.2-Hive中使用Substr拆分含中文乱码字符串报错异常分析

0754-5.16.2-Hive中使用Substr拆分含中文乱码字符串报错异常分析

作者头像
Fayson
发布2020-02-27 12:30:39
1.9K0
发布2020-02-27 12:30:39
举报
文章被收录于专栏:Hadoop实操Hadoop实操

作者:余枫

问题描述

从上游Oracle数据库中导出的携带中文乱码且编码集为ISO-8859-1的数据文件,将导出的数据文件导入到Hive表,在原始表的基础上通过创建视图,按照与上游接口约定的定长的方式拆分字段时报错,异常内容如下:

代码语言:javascript
复制
java.lang.RuntimeException: org.apache.hadoop.hive.ql.metadata.HiveException: Hive Runtime Error while processing row {"col":"0000004287|6413|....
Caused by: org.apache.hadoop.hive.ql.metadata.HiveException: java.nio.charset.UnmappableCharacterException: Input length = 1

问题复现

1.使用如下SQL语句创建外部表

代码语言:javascript
复制
CREATE EXTERNAL TABLE `test_error_S24`(`col` string COMMENT 'from deserializer')
ROW FORMAT SERDE 'org.apache.hadoop.hive.contrib.serde2.MultiDelimitSerDe'
WITH SERDEPROPERTIES ( 'field.delim'='|@|','serialization.encoding'='GBK')
STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat';

2.将异常数据文件加载到创建的外部表中

代码语言:javascript
复制
hadoop fs -put S24_ACCT20200107_error.txt /tmp

执行SQL语句将数据加载到test_error_S24表中

代码语言:javascript
复制
load data inpath '/tmp/S24_ACCT20200107_error.txt' into test_error_s24;

查看数据是否导入表中

3.使用如下SQL语句创建视图并使用定长方式拆分原始数据

代码语言:javascript
复制
CREATE VIEW `view_error_S24` AS 
select trim(decode(substr(encode(`test_error_S24`.`col`,'GBK'),1,10),'GBK')) as `XACCOUNT`,
trim(decode(substr(encode(`test_error_S24`.`col`,'GBK'),12,4),'GBK')) as `BANK`,
trim(decode(substr(encode(`test_error_S24`.`col`,'GBK'),17,20),'GBK')) as `BUSINESS`,
trim(decode(substr(encode(`test_error_S24`.`col`,'GBK'),38,4),'GBK')) as `CATEGORY`,
...
trim(decode(substr(encode(`test_error_S24`.`col`,'GBK'),2318,11),'GBK')) as `PAYTDY_LMT` from `test_error_S24`;

4.执行Select语句查看数据是否正常拆分时报错

查看Yarn上详细日志如下显示与第一章节问题描述一致

异常分析

1.Yarn作业的详细日志中有异常 “java.nio.charset.UnmappableCharacterException: Input length = 1”,引起该类异常的主要原因是处理了半个中文导致。

2.为什么会出现处理半个中文的问题?主要是由于在SQL语句中是通过定长的方式拆分字段,拆分字段是通过GBK编码集的方式进行定长拆分。

3.为什么拆分字符串会拆出半个中文?通过使用Java代码读取异常数据计算每条数据的length进行验证分析,结果如下:

GBK编码读取正常数据,显示每条数据的长度固定且中文字符未出现乱码

UTF-8编码读取正常数据,显示每条数据的长度发生变化且中文出现乱码

通过上述测试发现,主要是由于编码集原因导致拆分出半个中文的现象。因此在这个场景下要想正确的通过定长的方式解决数据拆分问题,只能以正确的中文编码集方式处理原始数据。

4.处理中文字符的编码有GB2312/GBK/GB18030等,常用的GBK和GB2312在这个时候并不能满足数据的正常解析,在这里尝试使用GB18030编码来对字符解析编码拆分测试

经过测试发现使用GB18030编码读取异常数据文件时,能正确的读取所有数据且不会出现中文乱码,通过上述的测试分析这里考虑在Hive建表及数据拆分时使用GB18030编码,接下来为问题解决及验证过程。

异常修复

1.修改建表语句将编码集调整为GB18030

代码语言:javascript
复制
CREATE EXTERNAL TABLE `test_gb18030`(`col` string COMMENT 'from deserializer')
ROW FORMAT SERDE 'org.apache.hadoop.hive.contrib.serde2.MultiDelimitSerDe'
WITH SERDEPROPERTIES ( 'field.delim'='|@|','serialization.encoding'='GB18030')
STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat';

2.重建视图,将视图中的编码类型修改为GB18030

代码语言:javascript
复制
CREATE VIEW `view_gb18030` AS select trim(decode(substr(encode(`test_gb18030`.`col`,'GB18030'),1,10),'GB18030')) as `XACCOUNT`,
trim(decode(substr(encode(`test_gb18030`.`col`,'GB18030'),12,4),'GB18030')) as `BANK`,
...
trim(decode(substr(encode(`test_gb18030`.`col`,'GB18030'),75,30),'GB18030')) as `ACC_NAME1`,
...
trim(decode(substr(encode(`test_gb18030`.`col`,'GB18030'),2318,11),'GB18030')) as `PAYTDY_LMT` from `test_gb18030`;

3.再次执行Select语句查看视图已可以正常拆分字段

总结

1.Hive建表时默认使用UTF-8编码,在处理中文编码的数据文件时,需要在建表语句中指定编码集,否则查询出来的数据会显示乱码。

2.对于通过定长方式拆分字符串的业务,必须知道上游业务系统的拆分规则,是以UTF-8编码拆分?还是GBK编码拆分?还是GB18030编码拆分?不同的编码方式计算出来的字符串长度也会有一定的差异。

3.处理中文字符编码方式有GB2312/GBK/GB1803等,GB18030兼容GBK,GBK兼容GB2312,因此在针对中文的解析时如果出错,可以使用最新的GB18030编码集进行解析。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Hadoop实操 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档