作者简介:严军(花名吉远),十年以上专注于数据库存储领域,对大数据、分布式、高并发、高性能、高可用有丰富的经验.主导过蚂蚁集团核心系统去IOE,数据库LDC单元化多活项目,常年负责蚂蚁重大数据库活动(如双11、双12、春节红包大促),现任阿里云分布式数据库架构师和云数据库架构师,专注于金融行业数字化转型过程中头部客户同构和重构数据库架构和迁移咨询工作。精通Oracle、Mysql、OceanBase数据库 ,擅长分布式数据库架构设计、数据库优化、容灾高可用方案设计。
问题描述
某保险客户使用OMS将Oracle 源端数据迁移到OceanBase oracle mode库,源端和目标端schemal 一致,字符集都是AL32UTF8,且字符串类型的长度定义 NLS_LENGTH_SEMANTICS 均为BYTE,但是迁移过程中发现报错,真实字段长度超过定义的字段长度
数据迁移报错报错信息如下:Data truncation: ORA-12899: value too large for column "xxxx"."xxxx"."ID15" (actual: 50, maximum: 40)。
问题分析
在oracle源端未找到长度超过40的数据,
但仔细查看OMS日志,发现出错记录乱码了如下图,产生该问题之间原因找到了,该字段附近有乱码,
我们在oracle源端使用默认的utf8 查询出来确实是乱码,但修改sqlplus客户端的环境变量nls_lang为gbk后,可以正常显示中文字符。
本质原因
oracle 源端,业务往utf8 的表中写入了其它字符格式的数据,导致oms 通过utf8字符读取出来就是乱码,进而写入目标端也是乱码,目前OMS工具无法识别此种特殊情况,需要应用程序进行校验,并在源端通过数据订正解决。
Oracle 端检查字段是否有乱码方法
这里需要使用asciistr 函数
ASCIISTR函数会把非ASCII 的字符转换成\xxxx 的格式,xxxx 是UTF-16的code unit。
示例
这里的’安’ 被转换成了\5B89,‘庆’被转换成了\5E86。
这里要注意一个特殊字符“\”,当它出现的时候转换后的码为“\005C”。
当然,我们也可以使用UNISTR函数,把asciistr 的结果反转回来
那么当我们的记录中存在中文乱码时
注意:
执行以上SQL 不要在sqlplus 里执行,sqlplus 受本地环境影响。到第三方的工具(PL/SQL DEV 或者Toad)里测试。那么当我们的中文记录变成乱码后,那么转成asciistr的值就会包含2种特殊符号: ?和\FFFD对应的符号。我们只需要匹配这2种符号,就可以判断记录里是否有乱码了。
解决方案
1、OceanBase 目标端扩容字段长度,解决OMS同步报错问题。
2、业务将Oracle源端乱码记录通过GBK字符集正确读出,再进行数据订正解决。
补充内容
在不同的数据库,因为字符集的不同,LENGTHB得到的值可能会不一样。如ZHS16GBK采用两个byte位来定义一个汉字。而在UTF8,采用3个byte。
通过这个示例,我们可以看出来,Length 和 Lengthb 函数的一个重要用处,就是用来判断记录值里是否有中文内容。
如果有中文,那么Length() != Lengthb()
如果没有中文,那么Length() == Lengthb()