Oracle-TiDB 迁移 - 生僻字乱码问题
作者: longzhuquan 原文来源:https://tidb.net/blog/d4d52358
背景
某去 O 场景业务上线测试,再执行某张表缩字段时报错。
现象
执行缩字段语句
报错信息
数据库版本
推测原因
TiDB 报错 ”incorrect string value ‘\xED’ for column ‘licenseno’”,一般来说此类报错出现在插入语句阶段,用户在插入与字符集集不匹配的非法字符时报错。而题主本次进行进行的缩字段语句,其原理操作步骤大致分为:将相关表数据由 KV 全部 LOAD 到 TIDB 内存中,在内存中进行修改数据,将修改完的数据进行合规性校验,检验完成后回填到 KV 中。
所以推测是在数据回填进行合法性校验时出现了错误。
查看数据库字符集
查询原表中是否含有非法字符
经过排查发现 “licenseno” 是一个车牌号,其字段类型为 “varchar(200)”,而表中确实存在非法字符,其中此字段的值为 “𤾂F XXXXX”。
此时怀疑是不是 TiDB 对于一些生僻词不识别或者转储有错误。于是进行人工校验
结果插入正常,怀疑错误。
此时回想数据来源,此数据是从 ORACLE 库中,通过 sqluldr2 导出成 CSV 文件,在通过 lighting 导入到 TiDB。合理怀疑链路过程某一环节有问题。单没有方向,只能从源头开始调查。
ORACLE 端
查询数据正常,但对于生僻字来说肉眼看不一定准确,固将所见字转换为 16 进制。
ORACLE 16 进制为 Typ=1 Len=10 CharacterSet=AL32UTF8: f0,a4,be,82,46,44,37,4d,36,38
TIDB 16 进制为 EDA193EDBE824644374D3638
明显看到两边存储码不相同。
验证结果,刚刚在 TIDB 中曾成功插入过“𤾂”字,查看手动插入的 16 进制,与 Oracle 相同,证明源端数据无问题。
继续排查,sqluldr2 导出文件,建立一张测试表,仅插入所需数据,使用工具导出。然后通过 VIM 命令 “:%!xxd” 查看字段 16 进制。明显看到此时转码已经出现问题,并不符合 ORACLE 存储的“f0,a4,be,82,46,44,37,4d,36,38
”,而是转换成了“EDA193EDBE824644374D3638
”。
为对比验证,现场使用 kettle 重新导出 csv 文件,以相同的方式查看“𤾂XXX”的 16 进制, 导出转码为 ”F0A4 BE824644374D3638” 正常转码。
由此判断为 sqluldr2 问题,查阅相关资料得知使用 sqluldr2 工具导出 csv 文件时。对于某些生僻字转码会存在转码错误的 bug,对于𤾂这类生僻字会转换成白 + 宛两个字的编码,但实际占有一个字符位置。从而导致存在非法字符。
另外存在疑问,导入时为何不进行数据合法性校验。咨询相关人员得到如下建议:使用 lighting 导入时, 若原 csv 文件为 utf8 或 binary 编码,则 lighting 导入时不进行任何设置。导入模式为 kv 时的方法,不存在问题。若导入时 csv 文件为其他编码格式,且未使用 data-character-set 参数时,则 lighting 会默认 csv 文件是二进制编码且直接使用 utf8 格式,此时转码可能会存在未知问题。
结语
使用三方工具还是存在一些风险,不过目前此方案确实迁移速度较快(相比较于 KTL 工具)。另外听 PingCap 正在推出自己的异构迁移工具,我司也正在进行内部测试,总体来说其速度相对于 kettle 还是比较快,但仍赶不上 sqluldr2 导出 lighting 的速度,且目前还存在一些问题,待我方测试完成后,各位如有兴趣,我会献上测评。
版权声明: 本文为 InfoQ 作者【TiDB 社区干货传送门】的原创文章。
原文链接:【http://xie.infoq.cn/article/3467cd3a07d67f5620cdd6c95】。文章转载请联系作者。
评论