GaussDB(DWS) GDS导入容错详解

举报
Kylin_Li 发表于 2020/12/15 15:14:18 2020/12/15
【摘要】 前言  GaussDB(DWS)作为一款数仓系统会经常面临大批量数据的导入导出需要在短暂的时间内处理上百T甚至PB级的数据,GaussDB针对这种场景提供了一款高性能的导入导出工具GDS(GaussDB data server),本片博文是基于读者已经对GDS的基本使用有所了解。  用户的数据质量在海量数据的导入场景下至关重要,但是这往往用户又很难保证数据的质量,因此GaussDB(DWS)...



前言

  GaussDB(DWS)作为一款数仓系统经常面临大批量数据的导入导出,并且需要在短时间内处理上百TB的数据,GaussDB针对这种场景提供了一款高性能的导入导出服务工具GDS(GaussDB data server)。本主要面向已经对GDS有了一定基础的同学,如果对GDS不是非常了解可以先查阅GaussDB(DWS)的官方文档。

  在海量数据的导入场景下由于用户数据源的多样性会降低数据的质量,因此就需要GaussDB(DWS)需要具备一定的数据容错能力,比如对非法字符的替换,字符集转换转换的容错,多余字段的忽略,字段缺失补齐等等。本文将会从最基础的计算机知识和一个一个的例子开始,让大家慢慢的理解GaussDB(DWS)的容错原理。



1.1. 如何判断文件的字符集编码?

要想回答清楚这个问题,需要从很基础的概念说起,比如字符集是什么,什么是字符,但是直观起见我就用一个例子来帮助大家理解,来看下们最常用的编辑器工具"vim"是如何处理字符,字符集问题的。

1. 编写一个hello.c程序,gcc hello.c -o hello 生成一个hello文件。

#include<stdio.h>
int main()
{
 printf("hello world.");
}

2. vim打开hello

^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@ð^D@^@^@^@^@^@Ð^D@^@^@^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@^L^@^@^@^@^@^@^@È^C@^@^@^@^@^@^M^@^@^@^@^@^@^@´^E@^@^@^@^@^@^Y^@^@^@^@^@^@^@^P^N`^@^@^@^@^@^[^@^@^@^@^@^@^@^H^@^@^@^@^@^@^@^Z^@^@^@^@^@^@^@^X^N`^@^@^@^@^@^\^@^@^@^@^@^@^@^H^@^@^@^@^@^@^@õþÿo^@^@^@^@<98>^B@^@^@^@^@^@^E^@^@^@^@^@^@^@^X^C@^@^@^@^@^@^F^@^@^@^@^@^@^@¸^B@^@^@^@^@^@
^@^@^@^@^@^@^@?^@^@^@^@^@^@^@^K^@^@^@^@^@^@^@^X^@^@^@^@^@^@^@^U^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^@^@^@^@^@^@^@^P`^@^@^@^@^@^B^@^@^@^@^@^@^@0^@^@^@^@^@^@^@^T^@^@^@^@^@^@^@^G^@^@^@^@^@^@^@^W^@^@^@^@^@^@^@<98>^C@^@^@^@^@^@^G^@^@^@^@^@^@^@<80>^C@^@^@^@^@^@^H^@^@^@^@^@^@^@^X^@^@^@^@^@^@^@   ^@^@^@^@^@^@^@^X^@^@^@^@^@^@^@þÿÿo^@^@^@^@`^C@^@^@^@^@^@ÿÿÿo^@^@^@^@^A^@^@^@^@^@^@^@ðÿÿo^@^@^@^@X^C@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@(^N`^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^F^D@^@^@^@^@^@^V^D@^@^@^@^@^@^@^@^@^@GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-36)^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^A^@8^B@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^B^@T^B@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^C^@t^B@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^D^@<98>^B@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^E^@¸^B@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^F^@^X^C@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^G^@X^C@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^H^@`^C@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@  ^@<80>^C@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@
@                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @
"hello" [noeol][converted] 4L, 8799C               

有经验的同学看到文件中大量的^@时候就会说,这是一个二进制文件,但是我们又是如何判断说这是一个二进制文件的呢? 

3. vim 命令模式中使用:set fileencoing 可以查看vim在当前的环境下提供了哪些用来处理数据的字符集。

@^E^@^@^@^@^@^@^@^X^C@^@^@^@^@^@^F^@^@^@^@^@^@^@¸^B@^@^@^@^@^@
^@^@^@^@^@^@^@?^@^@^@^@^@^@^@^K^@^@^@^@^@^@^@^X^@^@^@^@^@^@^@^U^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^@^@^@^@^@^@^@^P`^@^@^@^@^@^B^@^@^@^@^@^@^@0^@^@^@^@^@^@^@^T^@^@^@^@^@^@^@^G^@^@^@^@^@^@^@^W^@^@^@^@^@^@^@<98>^C@^@^@^@^@^@^G^@^@^@^@^@^@^@<80>^C@^@^@^@^@^@^H^@^@^@^@^@^@^@^X^@^@^@^@^@^@^@   ^@^@^@^@^@^@^@^X^@^@^@^@^@^@^@þÿÿo^@^@^@^@`^C@^@^@^@^@^@ÿÿÿo^@^@^@^@^A^@^@^@^@^@^@^@ðÿÿo^@^@^@^@X^C@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@(^N`^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^F^D@^@^@^@^@^@^V^D@^@^@^@^@^@^@^@^@^@GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-36)^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^A^@8^B@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^B^@T^B@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^C^@t^B@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^D^@<98>^B@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^E^@¸^B@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^F^@^X^C@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^G^@X^C@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^H^@`^C@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@  ^@<80>^C@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@
@                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @
  fileencodings=ucs-bom,utf-8,latin1  

可以看到当前linux语言环境下vim可以选用ucs-bom(windos为多字节字符提供的bom头机制),utf8,和latin1字符集来识别文件中的数据。

4. 继续在vim命令模式中使用:set fileencoding 可以查看当前vim最终选择了哪个字符集来编码数据。

^@^@^@^@È^C@^@^@^@^@^@^M^@^@^@^@^@^@^@´^E@^@^@^@^@^@^Y^@^@^@^@^@^@^@^P^N`^@^@^@^@^@^[^@^@^@^@^@^@^@^H^@^@^@^@^@^@^@^Z^@^@^@^@^@^@^@^X^N`^@^@^@^@^@^\^@^@^@^@^@^@^@^H^@^@^@^@^@^@^@õþÿo^@^@^@^@<98>^B@^@^@^@^@^@^E^@^@^@^@^@^@^@^X^C@^@^@^@^@^@^F^@^@^@^@^@^@^@¸^B@^@^@^@^@^@
^@^@^@^@^@^@^@?^@^@^@^@^@^@^@^K^@^@^@^@^@^@^@^X^@^@^@^@^@^@^@^U^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^@^@^@^@^@^@^@^P`^@^@^@^@^@^B^@^@^@^@^@^@^@0^@^@^@^@^@^@^@^T^@^@^@^@^@^@^@^G^@^@^@^@^@^@^@^W^@^@^@^@^@^@^@<98>^C@^@^@^@^@^@^G^@^@^@^@^@^@^@<80>^C@^@^@^@^@^@^H^@^@^@^@^@^@^@^X^@^@^@^@^@^@^@   ^@^@^@^@^@^@^@^X^@^@^@^@^@^@^@þÿÿo^@^@^@^@`^C@^@^@^@^@^@ÿÿÿo^@^@^@^@^A^@^@^@^@^@^@^@ðÿÿo^@^@^@^@X^C@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@(^N`^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^F^D@^@^@^@^@^@^V^D@^@^@^@^@^@^@^@^@^@GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-36)^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^A^@8^B@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^B^@T^B@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^C^@t^B@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^D^@<98>^B@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^E^@¸^B@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^F^@^X^C@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^G^@X^C@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^H^@`^C@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@  ^@<80>^C@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@
@                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @                                                                                                                                                                                                             @
  fileencoding=latin1              

可以看到vim最终选择了latin1来编码数据。其实vim 会顺序的从fileencodings中选择字符集,首先选择bom头发现编码失败,然后尝试使用utf8字符集编码发现还是失败,最后无奈之下只能使用latin1这种万能编码,总之先到先得。当前我使用linux系统语言环境字符集 LANG=en_US.UTF-8如果fileencodings中的编码全都编码失败,则会选择linux操作系统语言环境的字符集UTF8强制编码,编码失败则对非法字符做容错处理

1.1.2. 回过头再看文件中的'^@'是什么

    通过:help digraph-table命令可以查看vim的不可见字符的替换表,也就是说vim的屏显功能在实现的时候会将‘00’替换成两个可见字符‘^@’,所以并不是说包含"^@"的文    件就是二进制文件。

char  digraph   hex     dec     official name
^@      NU      0x00      0     NULL (NUL)
^A      SH      0x01      1     START OF HEADING (SOH)
^B      SX      0x02      2     START OF TEXT (STX)
^C      EX      0x03      3     END OF TEXT (ETX)
^D      ET      0x04      4     END OF TRANSMISSION (EOT)
^E      EQ      0x05      5     ENQUIRY (ENQ)
^F      AK      0x06      6     ACKNOWLEDGE (ACK)
^G      BL      0x07      7     BELL (BEL)
^H      BS      0x08      8     BACKSPACE (BS)
^I      HT      0x09      9     CHARACTER TABULATION (HT)
^@      LF      0x0a     10     LINE FEED (LF)
^K      VT      0x0b     11     LINE TABULATION (VT)
^L      FF      0x0c     12     FORM FEED (FF)
^M      CR      0x0d     13     CARRIAGE RETURN (CR)


digraph.txt.gz [Help][RO]                                                   

总结:所有的文件本质上都是一串二进制数据流,根本没有什么方绝对的方法可以判断一个文件的内容是什么字符集编码的,即使强如vim也只能尝试着去判断文件的编码,并顺其自然。我们没有什么好的方法去判断一个文件确切的是什么编码,因为文件的编码取决于数据是否满足相应的字符集编码


2.1. 非法字符和乱码

继续举例,现有一串字节流:"e4b8bc4042" 如果用utf8字符集编码则得到的结果是“丼@B”,如果用GBK编码则得到的是“涓粿B”,通过不同的字符集编码都能得到合法的字符串,那么哪一个叫乱码呢?

乱码:数据本身可以被多种字符集规则识别,校验,只是未按照预期的规则对数据编码。

如果上面的字节流稍加修改,"e4b8bc4042"->"e4b8bc",如果用utf8编码则得到的是“”,如果用GBK编码则得到的是""和一个非法字符"bc".

"bc"被认定为非法字符的原因是bc不能被GBK字符集规则识别,校验,‘bc’只是GBK字符的前半部分属于半个GBK字符。关于GBK的编码规则下面会做介绍。

非法字符:在GaussDB(DWS)中不满足指定的字符集规则或者不能转换成目标字符集的字符被称作非法字符。不能转换这个很好理解,我们都知道UTF8所包含的字符数远远大于GBK做能包含的字符数,因此肯定有某些字符包含在UTF8中但不包含在GBK中,比如: "䙑"它虽然不是一个GBK字符但是它一个utf8字符,因此对于GBK来说他就是一个非法字符。同时GaussDB(DWS)中还有有一个特殊的非法字符"0x00"

所以应用程序程序本身不可能去帮用户解决乱码的问题,因为在应用程序看来乱码也是一串合法的数据,解决乱码问题需要用户清楚自己文件内容的字符集编码,但是应用程序可以尽可能的去帮助用户解决或者规避非法字符问题。 


2.2. 非法字符和乱码处理

前面已经说了乱码本质上是不是数据的问题,只是因为选择错了字符集编码,但是一般程序可以对非法字符做一定的处理。我们还是先看下vim是如何处理非法字符的,以便于理解GaussDB(DWS)的处理方式。

前置:

1. linux 环境,语言环境 LANG=en_US.UTF-8

2. ~/.vimrc中添加  set fileencodings=""  ##强制vim不要从fileencodings中选择字符集编码数据,当fileencodings为空时会选择当前语言环境的字符集作为数据编码的字符集。

3. cat utf8.txt 添加内容并保存

中华人民共和国

4. vim -b utf8.txt   将“”的十六进制“e5 9b bd”认为修改为非法的UTF8字符 “e5 ff bd”,utf8的编码规则下面有说明。     :%!xxd -r 保存。

5. vim utf8.txt  查看更改后的数据

中华人民共和???

显然后面的非法字符‘e5 ff bd’的每一个字节都被替换成"?",这也正是vim的容错处理,事实上GaussDB(DWS)也是这么做的。

例如”0x00“在GaussDB(DWS) 中是一个非法字符。当用户设置compatible_illegal_chars=true时会将“0x00”替换成空格,将其他的非法字符替换成"?"否则将会报错

 invalid byte sequence for encoding "UTF8": 0x00


2.3. GaussDB(DWS)导入数据时的字符编码处理

GaussDB本质上也是一个应用程序因此和vim一样也会有字符和字符集的处理功能,但是出于功能和性能的考虑GaussDB(DWS)肯定不能像编辑器那样遍历字符集列表选择字符集。 GaussDB在使用GDS导入数据时候会让用户提供导入文件的字符集编码,如果用户没有提供,GaussDB(DWS)会认为用户数据的字符编码和数据库的字符编码是一致的。

目前GaussDB(DWS)数据库只支持库级别的编码设置,不能对表单独设置字符集,共支持如下四种编码方式:UTF8、GBK、Latin1、SQL_ASCII按导入文件字符编码方式跟数据库字符编码方式的组合,正交验证各种编码方式下,非法字符是否能容错导入

 

导入文件字符编码方式

数据库字符编码方式

UTF8

GBK

Latin1

SQL_ASCII

UTF8

UTF8编码校验

UTF8转换至GBK

UTF8转换至Latin1

UTF8编码校验

GBK

GBK转换至UTF8

GBK编码校验

GBK转换至Latin1

GBK编码校验

Latin1

0x00外均合法

不兼容

不校验/0x00外均合法

0x00外均合法

SQL_ASCII

UTF8编码校验

GBK编码校验

0x00外均合法

不校验/0x00外均合法

注:表中红色字体为8.0.0.5以下版本规格。

SQL_ASCII字符集

概述:ASCII的扩展字符集,8bit,可以存储除0x00外的任意字节流(GaussDB(DWS)的万能编码)

UTF8字符集

概述:Unicode字符集具体的编码实现,1-4字节变长字节编码

非法取值范围

1字节:[0x80, 0xc2]或者(0xf4, 0xff];

2字节:1字节为0xe0并且(2字节处于[0x00, 0xa0)或者(0xbf, 0xff]);

1字节为0xed并且(2字节处于[0x00, 0x80)或者(0x9f, 0xff]);

1字节为0xf0并且(2字节处于[0x00, 0x90)或者(0x8f, 0xff]);

1字节为0xf4并且(2字节处于[0x00, 0x80)或者(0xbf, 0xff]);

2字节处于[0x00, 0x80)或者(0xbf, 0xff]

3字节:3字节处于[0x00, 0x80)或者(0xbf, 0xff];

4字节:4字节处于[0x00, 0x80)或者(0xbf, 0xff]。

 

Gaussdb(DWS)GBK字符集

概述:国家简体中文字符集,使用2字节表示中文等字符
有效值范围:高字节从81到FE,低字节从40到FE,剔除xx7F一条线。


建议: GaussDB(DWS)支持GBK字符集编码,虽然GBK字符集比UTF8编码占用的存储空间小,但是GBK字符集编码的字符数远远小于UTF8, 如果用户将UTF8的数据导入GBK的库会存在数据丢失的风险,因此仍然不建议使用GBK字符集作为数据库编码。


3.1. GaussDB(DWS)在GDS导入数据的时候实现自动的进行编码转换

3.2. GaussDB(DWS) GDS导入自动转换过程

3.2.1. 数据读取 

DN读取原始数据d6d0b9fa

3.2.2. GBK编码校验

当外表参数和数据库参数不同时候数据库会对读取的数据做编码校验,GBK字符集的""首字节为'd6' 大于'7f'说明将要解析的字符为多字节字符,

且在GBK字符集规定的高字节范围(81->fe)内,说明该多字节字符的长度一定为2,因此还需要继续向下读取一个字节'd0',该字节范围在GBK字符集规定的低字节范围(00->fe)内,说明该多字节字符是一个合法的GBK字符。

3.2.3 编码转换

GaussDB(DWS)内部定义了很多编码映射文件,其中就包含了GBK和utf8的映射关系,然后从GBK->utf8的map文件中找到"d6d0"这个key就可以找到对应的UTF8的十六进制编码:"e4b8ad",最后将转换后的UTF8字符写入数据库中且该字符占用了3个字节。


4. GaussDB(DWS)容错措施

4.1. 数据错入错出

数据库和GDS的外表参数都设置成sql_ascii编码。sql_ascii字符集编码也叫错入错出编码,当数据库设置为sql_ascii编码后数据库不会对数据进行编码但是会校验数据中是否包含0x00,其他字符都会原样入库。

latin1和sql_ascii 都属于单字节字符编码,虽然可以给用户带来方便,但是强烈不建议用户使用这一类编码,因为没有了数据自身的校验约束机制,一旦某张表被写入了多种编码的数据,这张表的可维护性将变得极低。

4.2. 非法字符替换

 当用户设置compatible_illegal_chars=true时会将非法字符“0x00”替换成空格,否则会产生如下报错

 invalid byte sequence for encoding "UTF8": 0x00

当用户设置compatible_illegal_chars=true时GaussDB(DWS)会将不能被识别的字符和不能转换的字符替换成"?", 否则也会报错

## 不能被转换时
ERROR:  character with byte sequence 0xe4 0x99 0x91 in encoding "UTF8" has no equivalent in encoding "GBK"
## 不能被识别时
invalid byte sequence for encoding "UTF8": 0xe5 0xff 0xbd


4.3. 错误表机制

        错误表机制只会在数据导入时生效,也就是说只有执行insert into select * from "GDS外表" 时错误表机制才会生效,单纯的查询外表是不会触发错误表机制的。


         数据导入过程中发生的错误,一般分为数据格式错误和非数据格式错误。

  • 数据格式错误包括编码编码转换错误,不能识别的编码,和非法字符,多列少列,数据类型错误,非空约束,唯一约束,字段过长等等。


  • 在创建外表时,通过设置参数“LOG INTO error_table_name”,将数据导入过程中出现的数据格式错误信息写入指定的错误信息表error_table_name中。您可以通过以下SQL,查询详细错误信息。

    1
    postgres=# SELECT * FROM error_table_name;
    
    错误信息表结构如表1所示。
    表1 错误信息表

    列名称

    类型

    描述

    nodeid

    integer

    报错节点编号。

    begintime

    timestamp with time zone

    出现数据格式错误的时间。

    filename

    character varying

    出现数据格式错误的数据源文件名。

    rownum

    bigint

    在数据源文件中,出现数据格式错误的行号。

    rawrecord

    text

    在数据源文件中,出现数据格式错误的原始记录。

    detail

    text

    详细错误信息。

  • 非数据格式错误

    对于非数据格式错误,一旦发生将导致整个数据导入失败。您可以根据执行数据导入过程中,界面提示的错误信息,帮助定位问题,处理。

     根据获取的错误信息,请对照下表,处理数据导入错误。

错误信息

原因

解决办法

missing data for column "r_reason_desc"

  1. 数据源文件中的列数比外表定义的列数少。
  2. 对于TEXT格式的数据源文件,由于转义字符(\)导致delimiter(分隔符)错位或者quote(引号字符)错位造成的错误。

示例:目标表存在3列字段,导入的数据如下所示。由于存在转义字符“\”,分隔符“|”被转义为第二个字段的字段值,导致第三个字段值缺失。

BE|Belgium\|1

  1. 由于列数少导致的报错,选择下列办法解决:
    • 在数据源文件中,增加列“r_reason_desc”的字段值。
    • 在创建外表时,将参数“fill_missing_fields”设置为“on”。即当导入过程中,若数据源文件中一行数据的最后一个字段缺失,则把最后一个字段的值设置为NULL,不报错。
  2. 对由于转义字符导致的错误,需检查报错的行中是否含有转义字符(\)。若存在,建议在创建外表时,将参数“noescaping”(是否不对'\'和后面的字符进行转义)设置为true

extra data after last expected column

数据源文件中的列数比外表定义的列数多。

  • 在数据源文件中,删除多余的字段值。
  • 在创建外表时,将参数“ignore_extra_data”设置为“on”。即在导入过程中,若数据源文件比外表定义的列数多,则忽略行尾多出来的列。

invalid input syntax for type numeric: "a"

数据类型错误。

在数据源文件中,修改输入字段的数据类型。根据此错误信息,请将输入的数据类型修改为numeric

null value in column "staff_id" violates not-null constraint

非空约束。

在数据源文件中,增加非空字段信息。根据此错误信息,请增加“staff_id”列的值。

duplicate key value violates unique constraint "reg_id_pk"

唯一约束。

  • 删除数据源文件中重复的行。
  • 通过设置关键字“DISTINCT”,从SELECT结果集中删除重复的行,保证导入的每一行都是唯一的。

1

postgres=# INSERT INTO reasons SELECT DISTINCT * FROM foreign_tpcds_reasons;

value too long for type character varying(16)

字段值长度超过限制。

在数据源文件中,修改字段值长度。根据此错误信息,字段值长度限制为VARCHAR2(16)


4.4. conflict_delimiter参数解决字段分割符被非法字符"感染"

conflict_delimiter 参数解决源数据中因为某种原因只包含了多字节字符的前半部分,导致该半个字节和分隔符被编码成一个字符导致的数据格式错误

例如:假设有一行GBK数据"3|+|膢+|20191212"要被导入到UTF8的数据库,用户自定义的字段分割符为"|+|"。

这行GBK数据和十六进制对应关系如下

33 7C 2B 7C C4 7C 2B 7C 323031393132313

↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓

3 | + | ** | + | 20191212


当导入UTF8数据库时,数据库会发生如下转换。

GBK -> UTF8

33->33 (3)

7C->7C (|)

2B->2B (+)

7C->7C(|)

C47C-> E886A2(膢)

2B->2B(+)

7C->7C(|)

323031393132313-> 20191212

由于用户定义的字段分割符号为"|+|",因此对改行数据分割后获取的数据是3,膢+|20191212,

实际上用户可能要获得的是3,?,20191212。

"膢" 这个字符其实是一个用户不期望的半个字符,和用户定义的分割符“|+|”中的“|”产生了冲突,导致数据错乱或者导入失败。

如果用户想要忽略这种字符可以使用conflict_delimiterr配合compatible_illegal_chars参数将“C4”这个半个GBK字符准换成“?”导入UTF8数据库。

20210126-093950(WeLinkPC).png

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

举报
请填写举报理由
0/200