從上遊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編碼集進行解析。