作者: longzhuquan 原文来源: https://tidb.net/blog/d4d52358
背景
某去O场景业务上线测试,再执行某张表缩字段时报错。
现象
执行缩字段语句
ALTER TABLE schemaname.tablename MODIFY COLUMN licenseno varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL COMMENT '发动机号' ;
报错信息
"incorrect string value '\xED' for column ‘licenseno’"
数据库版本
推测原因
TiDB报错"incorrect string value '\xED' for column ‘licenseno’",一般来说此类报错出现在插入语句阶段,用户在插入与字符集集不匹配的非法字符时报错。而题主本次进行进行的缩字段语句,其原理操作步骤大致分为:将相关表数据由 KV 全部 LOAD 到 TIDB 内存中,在内存中进行修改数据,将修改完的数据进行合规性校验,检验完成后回填到 KV 中。
所以推测是在数据回填进行合法性校验时出现了错误。
查看数据库字符集
character_set_client utf8mb4
character_set_connection utf8mb4
character_set_database utf8mb4
character_set_filesystem binary
character_set_results
character_set_server utf8mb4
character_set_system utf8
character_sets_dir /usr/local/mysql-5.6.25-osx10.8-x86_64/share/charsets/
查询原表中是否含有非法字符
select _tidb_rowid, convert(a using utf8mb4) from table where convert(a using utf8mb4) != a;
经过排查发现 “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 的速度,且目前还存在一些问题,待我方测试完成后,各位如有兴趣,我会献上测评。