【迁移ORACLE数据到MogDB/openGauss时的字符集问题】

news/2024/7/9 21:08:19 标签: 数据库, postgresql, 运维

一、问题概述

ORACLE数据库在存储数据的时候,有时候会存在这样一种现象,一张表里的数据,既存在UTF8字符的,也存在GBK字符的,同时还有可能存在乱码数据。

NLS_CHARACTERSET是数据库字符集,NLS_NCHAR_CHARACTERSET是国家字符集,NLS_LANG 是 Oracle 数据库客户端的一个环境变量。

这种问题在于ORACLE对于规定了一种字符集后,对于插入的数据并没有强校验(garbage-in–garbage-out)。但是对于PG系的数据库数据库的字符集在最开始创建数据库的时候就指定了,而且一般情况下其中的表数据受字符集的严格校验(SQL_ASCII除外)。

这也就导致了,原本是什么类型的字符正常可以导入到对应的字符集的库里,而原本直接存入ORACLE库里的和表原本的字符集不匹配的其他类型的字符可能无法转换,此外乱码数据可能直接不能转换到PG系的库里。(ORACLE数据库可以存储乱码数据,PG系数据库有严格校验)

一些常见场景如下:

1)如果恰巧数据库的字符集也是UTF8, 那么Oracle就不作任何转换直接插入到数据中.

2)如果指定NLS_LANG是utf8, 但是输入的却是zhs16gbk的编码, 那么Oracle也会不作任何转换, 将ZHS16GBK的字符编码直接存入数据库

3)如果数据库的字符是AL32UTF8, 您指定NLS_LANG为ZHS16GBK, 但是, 您真正输入的是UTF8的字符, 那么,Oracle会把您输入的UTF8字符当作ZHS16GBK字符转换为UTF8存入数据库. 这种情况会出现乱码。

4)工具(putty/securecrt等等各种SSH客户端等等)设置的字符集可能导致乱码或者编码转换,客户端字符集 NLS_LANG 和 个人工具显示的字符集应该一致。

二、问题处理方案

1、使用SQL_ASCII字符集的数据库

如果不考虑中文显示以及乱码显示,可以直接在目标端建一个SQL_ASCII字符集的数据库,那么所有的数据均可以导入到新的PG系的数据库里,这样就不会有编码转换。这个设置基本不用来声明所使用的编码,因为此声明会忽略编码。在大多数情况下,如果使用了任何非ASCII数据,那么我们不建议使用SQL_ASCII,因为openGauss/MogDB无法转换或者校验非ASCII字符。

2.根据原库的编码,创建相同字符集编码的数据库

ORACLE在创建数据库的时候,需要指定字符集。虽然上述的一些原因导致ORACLE在指定了一种字符集的数据库后,还可能存在其他类型的字符和乱码数据,但这明显是不符合规范的,因为抛开其他类型的数据,单单乱码数据就不能算作有效数据,而且其他类型的数据,属于违反了最初的规划,也不能算作正常的数据。

因为不同的字符类型,对应存储的不同的字节长度是不一样的,例如一个汉字,在UTF8里存储为3位字节,而在GBK里存储为2个字节。

比如一个“你”字,用UTF-8存储在ORACLE数据里,是占用3个字节,是“\xe4bda0”
而用GBK存储在ORACLE数据里,是占用2个字节,是“\xc4e3”

所以长度是不一致的,在它转换到PG系的数据库里时,如果不能分辨出对应的是哪种类型的字符的话,只根据固定的一种类型的字符集的话,会存在部分数据的编码无法转换,在使用MTK(云和恩墨的迁移工具)迁移数据的时候,可能类似于这种的报错。

image.png

对于这种报错,因为本身在原库写入的时候就存在问题,如果数据库层大量修正的话,会耗费很大精力,因此,建议规范处理流程,仅根据数据库的字符集迁移“正确”的数据,其余的有问题的数据,可以在迁移过程找到对应的位置,或者进一步判断出该类数据原本应该属于什么样的字符集,然后这些问题数据让应用自行修正。

(1)查询存储的编码

在遇到问题数据的时候,我们可以根据其他正常的列,定位到对应的数据行,使用ORACLE的dump函数,查看其在ORACLE数据库里存储的编码格式

select dump(sms_content,1016) code,sms_content from  tab_xxx where mo_id=‘123123123132312’

CODE						   SMS_CONTENT
-------------------------------------------------- ----------------------------------------------------------------------------------------------------
Typ=1 Len=64 CharacterSet=ZHS16GBK: 78,20,20,20,20 x             :(;                    "“           ¥    ”€“"
,20,20,20,20,20,20,20,20,20,3a,28,a3,bb,20,20,20,2
0,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
22,a1,b0,20,20,20,20,20,20,20,20,20,20,20,a3,a4,20
,20,20,20,a1,b1,80,a1,b0,22

(2)根据函数或者工具检验出真实的字符编码

然后可以把有问题的数据对应的编码,根据不同位数进行转换,看转换的数据是否是正常可读的数据,原本最开始查询的数据不用额外关注,因为可能受本地终端字符集的影响,可能本身存储的是正常的,例如,我查询到了问题列的数据导出的结果是如下。

Pasted Graphic 4.png

取出部分编码,进行转换,"\xe59089e69e97"根据GBK按照两位字节的形式进行转换,得到的是“鍚夋灄”,而根据UTF-8的类型进行转换,得到的是“吉林”。因此原本这条数据本身应该是UTF-8的字符集。

可以使用相关函数进行转换,也可以使用相关在线工具,例如:字符 编码/解码 - 在线工具

postgres=# select convert_from(decode('e59089e69e97','hex')::bytea,'utf-8');
 convert_from 
--------------
 吉林
(1 row)

而本身这条数据正确显示的时候,应该为

Pasted Graphic 3.png

根据此类方法,以及对业务数据的了解,可以分辨出原本数据应该属于的字符集类型和非乱码的数据。

(3)MTK迁移过程需要处理的部分

手动使用COPY处理

因为MTK迁移的过程可以指定每批次导入数据的条数,在导入数据存在此类字符集编码问题的时候,我们一般会带上igErrorData参数,会把有问题的整个批次的数据全部回滚,而其余数据继续入库。
这个参数会在回滚那个问题批次数据的同时,把这个批次的数据记录到错误文件里,产生csv文件.

需要注意的是,这个csv是MTK分的迁移批次,每个批次的数据条数是一致的,可能在这一个批次里,仅存在一条问题数据,但它会把整个批次的数据全部放到csv里。可以先自行查看csv筛出问题数据,或者定位问题数据。

这个时候,我们关注的是这个csv里的正常数据,这个csv可以使用copy命令进行绕入。问题数据也可以暂时性先倒入,但是我们不关注因为字符集问题导致的查询乱码现象。

后续可以使用openGauss/MogDB的copy带有的COMPATIBLE_ILLEGAL_CHARS参数,这个参数允许导入非法字符容错参数。此语法仅对COPY FROM导入有效。但是在导入这些存在问题的数据的时候,存在一个规则,即对于’\0’,容错后转换为空格,其他非法字符,容错后转换为问号。这部分数据即我们上文提到的属于原库就错误写入的,后续应该让业务修正。

image.png

MTK自动处理

MTK(v2.9.4)新增的compatibleIlLegaLChars选项决定了mogdb/openGauss使用MTK迁移过程里,copy是否带COMPATIBLE_ILLEGAL_CHARS选项。

如果开启后,则会直接进行入上述的转换方式,写入数据,当然问题数据需要业务修正。(一般建议手动先产生错误的数据的csv后,定位有问题数据的位置后,再重新做一次自动处理的迁移)


http://www.niftyadmin.cn/n/5157757.html

相关文章

在jupyter中使用R

如果想在Jupyter Notebook中使用R语言,以下几个步骤操作可行: 1、启动Anaconda Prompt 2、进入R的安装位置,切换到R的安装位置:D:\Program Files\R\R-3.4.3\bin,启动R,具体代码操作步骤如下,在…

php多实例

文章目录 安装phpftp扩展查看扩展是否安装成功php-fpm多实例(nginxphp-fpm) 安装php cd /usr/local/src/php-5.6.35/ext/ftp/ /usr/local/php56/bin/phpize ./configure --with-php-config/usr/local/php56/bin/php-config echo $? make echo $? make installftp扩展 vim /…

手机怎么打包?三个方法随心选!

有的时候,电脑不在身边,只有随身携带的手机,这个时候又急需把文件打包发送给同事或者同学,如何利用手机操作呢?下面介绍了具体的操作步骤。 一、通过手机文件管理自带压缩功能打包 1、如果是iOS系统,就在手…

C++ reference

cppreference.com 《现代C语言核心特性解析》 这是一本 C 进阶图书,全书分为 42 章,深入探讨了从 C11 到 C20 引入的核心特性。 本书不仅通过大量的实例代码讲解特性的概念和语法,还从编译器的角度分析特性的实现原理,让读者…

数字化转型系列主题:商业画布分析方法与样例说明

定义 商业画布分析(Business Canvas Analysis)是一种用来评估商业模型的工具和方法。它通过将商业模型的各个关键要素绘制在一个画布上,帮助企业或创业者更好地理解和规划他们的业务。 商业画布分析最常用的框架是"商业模型画布"…

DataFrame的基本用法

目录 一、定义/读取 DataFrame 1.定义DataFrame 2.定义一个空的DataFrame 3.从csv中读取DataFrame 二、读取行列 1.直接读取行列 2.使用 .loc() 读取行列 3.使用 .iloc() 读取行列 4.读取前 n 行 5.读取并修改列名 6.读取并修改行名 7.df的转置 三、删除和增加 …

测试老鸟整理,Postman加密接口测试-Rsa/Aes对参数加密(详细总结)

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 一些问题 postma…

GET 请求和 POST 请求

浅析HTTP中请求GET/POST - 知乎 (zhihu.com) 什么是GET GET:从服务器请求数据后获取服务端数据 常见发起get请求的方式: URL、src/href、表单(form) 格式: index.php?userNamejack&password123 语法(keyvalue&keyva…