
技术分享 | load data导致主键丢失的神秘问题
● 1、发现问题
● 2、复现问题
● 3、检查导入文件
● 4、问题原因
● 5、处理问题
● 6、总结
1、发现问题
在一次数据迁移的任务中,小玲将源端数据库中数据导出为CSV文件,然后通过 load data 导入数据到MySQL,结果惊奇地发现id字段丢失了,就像这个样子:
目标数据库版本与表结构如下:
小玲冷静一下之后,通过以下语句验证了主键id并没有真的丢失,似乎只是出现了某种显示错误:
于是小玲决定排查整个操作流程,搞清楚问题的原因。
2、复现问题
首先创建一个表t1
在windows环境下,通过记事本准备一个新的测试文件t1.txt,在linux环境下查看t1.txt内容如下:
进行load,并查看数据。发现并没有错乱。
但是有点奇怪的就是右侧的边线不见了,手动插入一个数据,再查询看下数据情况。
解析一下binlog日志
可以明显看出来实际插入txt的数据test6的时候并不是test6,而是test\r。而我们手动插入的test7,则确实为test7。
再查看下实际插入的数据的16进制值。
load 进来的数据后面比手动插入的正常数据多了0D。
到这里基本上就很明显了,是由于txt文件中的符号导致的。而不同于开头查询的结果显示那么不规范的原因,是因为col2的length都是5。导入length不同的数据,就可以明显看出差异。
3、检查导入文件
确认是导入的文件问题,我们查看下这个文件的16进制的情况。
这里使用的是vscode插件hexdump,结果如下
观察可以看到除了0D还有0A
可以得知,导入的txt文件中,每一行的末尾是 \r\n 作为换行的。
在不同的系统中,对于换行符有着不同的表示方式。
以下来自维基百科
应用软件以及操作系统对于换行字符的表示方式:
以ASCII为基础的或兼容的字符集使用分别LF(Line feed,U+000A)或CR(Carriage Return,U>+000D)或CR+LF;下面列出各系统换行字符编码的列表
LF:在Unix或Unix兼容系统(GNU/Linux,AIX,Xenix,Mac OS X,...)、BeOS、Amiga、RISC OS
CR+LF:DOS(MS-DOS、PC-DOS等)、微软视窗操作系统(Microsoft Windows)、大部分非Unix的系统
CR:Apple II家族,Mac OS至版本9
4、问题原因
由上可知,是由于txt文本中,每一行的末尾使用 \r\n 作为换行,而linux系统使用 \n 作为换行,因此 \r 作为一个字符被插入到表中。
\r 在mysql中被处理为 A carriage return character,因此会出现文章头的情况,查询结果不规范。
5、处理问题
在使用load_data导入数据的时候,可以使用 lines terminated by '\r\n' 来告诉mysql,\r\n 是整个作为换行符来使用的。
这样来重新load一下。
解析下二进制日志看下实际插入的情况
插入的数值正确。
6、总结
整个问题是由于linux系统和windows系统的换行符不一致导致的。再导入时候需要思考不同系统之间的文件是否存在处理差异,避免导致不可预知的后果。
[参考文档]
1.String Literals(https://dev.mysql.com/doc/refman/8.0/en/string-literals.html)
2.LOAD DATA Statement(https://dev.mysql.com/doc/refman/8.0/en/load-data.html)
Enjoy GreatSQL :)
文章转载自公众号:GreatSQL社区
