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

作者:余枫

问题描述

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

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语句创建外部表

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.将异常数据文件加载到创建的外部表中

hadoop fs -put S24_ACCT20200107_error.txt /tmp

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

load data inpath '/tmp/S24_ACCT20200107_error.txt' into test_error_s24;

查看数据是否导入表中

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

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

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

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编码集进行解析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值