欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答

  • GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。
  • 1、发现问题

  • 2、复现问题

  • 3、检查导入文件

  • 4、问题原因

  • 5、处理问题

  • 6、总结

1、发现问题

在一次数据迁移的任务中,小玲将源端数据库中数据导出为CSV文件,然后通过 load data 导入数据到MySQL,结果惊奇地发现id字段丢失了,就像这个样子:

mysql> select * from t2;
+----+-------+---------------------+
| id | col1 | col2 |
+----+-------+---------------------+
|
|| TfdESTA
|TESTA
|4 | TEfdfdSTA
| 5 | TEST5 | TESfddfdsfdsfdsfTA
|TEST6 | TESffdfdfddTA
+----+-------+---------------------+
6 rows in set (0.00 sec)

目标数据库版本与表结构如下:

mysql> select @@version;
+-----------+
| @@version |
+-----------+
| 8.0.25 |
+-----------+
1 row in set (0.00 sec) mysql> show create table t2;
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| t2 | CREATE TABLE `t2` (
`id` int NOT NULL AUTO_INCREMENT,
`col1` varchar(69) DEFAULT NULL,
`col2` varchar(79) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+-------+----------------------------------------------------------------------------------

小玲冷静一下之后,通过以下语句验证了主键id并没有真的丢失,似乎只是出现了某种显示错误:

mysql> select * from t2 where id=1;
+----+-------+------+
| id | col1 | col2 |
+----+-------+------+
|1 | TEST1 | TA
+----+-------+------+
1 row in set (0.00 sec)

于是小玲决定排查整个操作流程,搞清楚问题的原因。

2、复现问题

首先创建一个表t1

mysql> CREATE TABLE `t1` (
-> `id` int NOT NULL AUTO_INCREMENT,
-> `col1` varchar(60) DEFAULT NULL,
-> `col2` varchar(70) DEFAULT NULL,
-> PRIMARY KEY (`id`)
-> );
Query OK, 0 rows affected (0.01 sec)

在windows环境下,通过记事本准备一个新的测试文件t1.txt,在linux环境下查看t1.txt内容如下:

great@great-PC:~/Downloads/windows$ cat t1.txt
test1,test1
test2,test2
test3,test3
test4,test4
test5,test5
test6,test6

进行load,并查看数据。发现并没有错乱。

mysql> load data infile "/home/great/Downloads/windows/t1.txt" \
into table t1 FIELDS TERMINATED BY ',' (col1,col2);
Query OK, 6 rows affected (0.01 sec)
Records: 6 Deleted: 0 Skipped: 0 Warnings: 0 mysql> select * from t1;
+----+-------+--------+
| id | col1 | col2 |
+----+-------+--------+
| 1 | test1 | test1
| 2 | test2 | test2
| 3 | test3 | test3
| 4 | test4 | test4
| 5 | test5 | test5
| 6 | test6 | test6
+----+-------+--------+
6 rows in set (0.00 sec)

但是有点奇怪的就是右侧的边线不见了,手动插入一个数据,再查询看下数据情况。

mysql> insert into t1(col1,col2) values('test7','test7');
Query OK, 1 row affected (0.00 sec) mysql> select * from t1;
+----+-------+--------+
| id | col1 | col2 |
+----+-------+--------+
| 1 | test1 | test1
| 2 | test2 | test2
| 3 | test3 | test3
| 4 | test4 | test4
| 5 | test5 | test5
| 6 | test6 | test6
| 7 | test7 | test7 |
+----+-------+--------+
7 rows in set (0.00 sec) mysql> select * from t1 where id=7;
+----+-------+-------+
| id | col1 | col2 |
+----+-------+-------+
| 7 | test7 | test7 |
+----+-------+-------+
1 row in set (0.00 sec)

解析一下binlog日志

...

### INSERT INTO `test`.`t1`
### SET
### @1=6 /* INT meta=0 nullable=0 is_null=0 */
### @2='test6' /* VARSTRING(240) meta=240 nullable=1 is_null=0 */
### @3='test6\r' /* VARSTRING(280) meta=280 nullable=1 is_null=0 */
# at 1223
#210923 11:19:09 server id 12345 end_log_pos 1250 Xid = 271
COMMIT/*!*/; ... ### INSERT INTO `test`.`t1`
### SET
### @1=7 /* INT meta=0 nullable=0 is_null=0 */
### @2='test7' /* VARSTRING(240) meta=240 nullable=1 is_null=0 */
### @3='test7' /* VARSTRING(280) meta=280 nullable=1 is_null=0 */
# at 1500
#210923 11:19:15 server id 12345 end_log_pos 1527 Xid = 272
COMMIT/*!*/;

可以明显看出来实际插入txt的数据test6的时候并不是test6,而是test\r。而我们手动插入的test7,则确实为test7。

再查看下实际插入的数据的16进制值。

mysql> select id,hex(col2) from t1;
+----+--------------+
| id | hex(col2) |
+----+--------------+
| 1 | 74657374310D |
| 2 | 74657374320D |
| 3 | 74657374330D |
| 4 | 74657374340D |
| 5 | 74657374350D |
| 6 | 74657374360D |
| 8 | 7465737437 |
+----+--------------+
7 rows in set (0.00 sec)

load 进来的数据后面比手动插入的正常数据多了0D。

mysql> select  hex('\r') from t1;
+-----------+
| hex('\r') |
+-----------+
| 0D |
| 0D |
| 0D |
| 0D |
| 0D |
| 0D |
| 0D |
+-----------+
7 rows in set (0.00 sec) mysql> select unhex('0D') from t1;
+-------------+
| unhex('0D') |
+-------------+
|
|
|
|
|
|
|
+-------------+
7 rows in set (0.00 sec)

到这里基本上就很明显了,是由于txt文件中的符号导致的。而不同于开头查询的结果显示那么不规范的原因,是因为col2的length都是5。导入length不同的数据,就可以明显看出差异。

mysql> load data infile "/home/great/Downloads/windows/oracle_objects.txt" \
into table t1 FIELDS TERMINATED BY ',' (col1,col2);
Query OK, 2088 rows affected (0.02 sec)
Records: 2088 Deleted: 0 Skipped: 0 Warnings: 0 mysql> select * from t1 limit 30 ;
+----+-----------------------------+------------+
| id | col1 | col2 |
+----+-----------------------------+------------+
| test1 | test1
| test2 | test2
| test3 | test3
| test4 | test4
| test5 | test5
| test6 | test6
| 8 | test7 | test7 |
| A | TABLE
| A1 | TABLE
| AAA | TABLE
| ABC123 | TABLE
| ABCDEF | TABLE
| ACTIVE_ALARMS | TABLE
|15 | ADDAUTH | PROCEDURE
|16 | ADDROLEAUTH | PROCEDURE
| AGENT_AVAIL_PRIV | TABLE
| AGE_STAT_FORBIDTIMELOGIN | TABLE
| AGE_STAT_NOTFORBIDTIMELOGIN | TABLE
| AGE_STAT_QUERYLARGE | TABLE
| APP_SCHEDULE_INFO | TABLE
|22 | AP_CLEARMUTEXROLL | PROCEDURE
|23 | AP_CLEARROLEBYSTSNUM | PROCEDURE
|24 | AP_CLEARROLEBYTEL | PROCEDURE
|25 | AP_CLEARSUPERROLL | PROCEDURE
|26 | AP_CREATE_RDDLV_FILE | PROCEDURE
|27 | AP_DEALACTLOG_OPERIP | PROCEDURE
|28 | AP_LOC_CRMBI_MSGSEND_LOG | PROCEDURE
|29 | AP_MENDCLICK | PROCEDURE
|30 | AP_PUB_UNLOCK_VERIFYCODE | PROCEDURE
|31 | AP_SETDBUSERANDPASS | PROCEDURE
+----+-----------------------------+------------+
30 rows in set (0.00 sec)

3、检查导入文件

确认是导入的文件问题,我们查看下这个文件的16进制的情况。

这里使用的是vscode插件hexdump,结果如下

  Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000: 74 65 73 74 31 2C 74 65 73 74 31 0D 0A 74 65 73 test1,test1..tes
00000010: 74 32 2C 74 65 73 74 32 0D 0A 74 65 73 74 33 2C t2,test2..test3,
00000020: 74 65 73 74 33 0D 0A 74 65 73 74 34 2C 74 65 73 test3..test4,tes
00000030: 74 34 0D 0A 74 65 73 74 35 2C 74 65 73 74 35 0D t4..test5,test5.
00000040: 0A 74 65 73 74 36 2C 74 65 73 74 36 0D 0A .test6,test6..

观察可以看到除了0D还有0A

mysql> select  hex('\n') ;
+-----------+
| hex('\n') |
+-----------+
| 0A |
+-----------+
1 row in set (0.00 sec)

可以得知,导入的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一下。

mysql> show master status;
+---------------+----------+--------------+------------------+---------------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+----------+--------------+------------------+---------------------------------------------+
| binlog.000013 | 192 | | | b5457ec7-f50a-11eb-ac22-2cf05daaf63e:1-6182 |
+---------------+----------+--------------+------------------+---------------------------------------------+
1 row in set (0.00 sec) mysql> load data infile "/home/great/Downloads/windows/t1.txt" \
into table t1 fields terminated by ','lines terminated by '\r\n'(col1,col2);
Query OK, 6 rows affected (0.00 sec)
Records: 6 Deleted: 0 Skipped: 0 Warnings: 0 mysql> select * from t1;
+----+-------+-------+
| id | col1 | col2 |
+----+-------+-------+
| 1 | test1 | test1 |
| 2 | test2 | test2 |
| 3 | test3 | test3 |
| 4 | test4 | test4 |
| 5 | test5 | test5 |
| 6 | test6 | test6 |
+----+-------+-------+
6 rows in set (0.00 sec)

解析下二进制日志看下实际插入的情况

...
### INSERT INTO `test`.`t1`
### SET
### @1=6 /* INT meta=0 nullable=0 is_null=0 */
### @2='test6' /* VARSTRING(240) meta=240 nullable=1 is_null=0 */
### @3='test6' /* VARSTRING(280) meta=280 nullable=1 is_null=0 */
# at 532
#210923 14:39:18 server id 12345 end_log_pos 559 Xid = 385
COMMIT/*!*/;
...

插入的数值正确。

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

文章推荐:

技术分享 | MGR最佳实践(MGR Best Practice)

https://mp.weixin.qq.com/s/66u5K7a9u8GcE2KPn4kCaA

技术分享 | 万里数据库MGR Bug修复之路

https://mp.weixin.qq.com/s/IavpeP93haOKVBt7eO8luQ

Macos系统编译percona及部分函数在Macos系统上运算差异

https://mp.weixin.qq.com/s/jAbwicbRc1nQ0f2cIa_2nQ

技术分享 | 利用systemd管理MySQL单机多实例

https://mp.weixin.qq.com/s/iJjXwd0z1a6isUJtuAAHtQ

产品 | GreatSQL,打造更好的MGR生态

https://mp.weixin.qq.com/s/ByAjPOwHIwEPFtwC5jA28Q

产品 | GreatSQL MGR优化参考

https://mp.weixin.qq.com/s/5mL_ERRIjpdOuONian8_Ow

关于 GreatSQL

GreatSQL是由万里数据库维护的MySQL分支,专注于提升MGR可靠性及性能,支持InnoDB并行查询特性,是适用于金融级应用的MySQL分支版本。

Gitee:

https://gitee.com/GreatSQL/GreatSQL

GitHub:

https://github.com/GreatSQL/GreatSQL

微信&QQ群:

可搜索添加GreatSQL社区助手微信好友,发送验证信息“加群”加入GreatSQL/MGR交流微信群

QQ群:533341697

微信小助手:wanlidbc

本文由博客一文多发平台 OpenWrite 发布!

技术分享 | load data导致主键丢失的神秘问题的更多相关文章

  1. (转)mysql自增列导致主键重复问题分析

    mysql自增列导致主键重复问题分析...  原文:http://www.cnblogs.com/cchust/p/3914935.html 前几天开发童鞋反馈一个利用load data infile ...

  2. mysql自增列导致主键重复问题分析。。。

    前几天开发童鞋反馈一个利用load data infile命令导入数据主键冲突的问题,分析后确定这个问题可能是mysql的一个bug,这里提出来给大家分享下.以免以后有童鞋遇到类似问题百思不得其解,难 ...

  3. 成都项目中因为MYSQL与SSDB备分时间不一致,导致主键产生器错误解决一例

    -- JFinal错误提示 Duplicate entry '1791361-1823391' for key 'PRIMARY' -- 1.查看SSDB的主键生成器值ssdb 127.0.0.1:8 ...

  4. MySQL 中的自增主键

    MySQL 的主键可以是自增的,那么如果在断电重启后新增的值还会延续断电前的自增值吗?自增值默认为1,那么可不可以改变呢?下面就说一下 MySQL 的自增值. 特点 保存策略 1.如果存储引擎是 My ...

  5. Hibernate(4)——主键生成策略、CRUD 基础API区别的总结 和 注解的使用

    俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及的知识点总结如下: hibernate的主键生成策略 UUID 配置的补充:hbm2ddl.auto属性用法 注解还是配置文件 h ...

  6. 深入理解Redis主键失效原理及实现机制

    http://blog.jobbole.com/71095/ 对于缓存失效,不同的缓存有不同的处理机制,可以说是大同中有小异,作者通过对Redis 文档与相关源码的仔细研读,为大家详细剖析了 Redi ...

  7. Hibernate联合主键映射

    1.联合主键的映射规则 1) 类中的每个主键属性都对应到数据表中的每个主键列. Hibernate要求具有联合主键的实体类实现Serializable接口,并且重写hashCode与equals方法, ...

  8. 二、Sql Server 基础培训《进度2-关于主键(知识点学习)》

    学习作业2: 问题1:主键都有哪些方式?   问题2:本次实战案例建立的主键采用哪种方式?   问题3:猜猜金蝶K3WISE建立的主键采用哪种方式?   问题4:谈谈手工主键增长设置具体实现思路?(选 ...

  9. Redis主键失效 - 原理及实现机制

    [数据记录过期源码][http://blog.csdn.net/yuanrxdu/article/details/21233047] [http://blog.jobbole.com/71095/] ...

随机推荐

  1. 【python】python连接Oracle数据库

    python连接Oracle数据库 查看Oracle版本 select * from v$version 下载对应版本的InstantClient 下载网址 InstantClient 1.解压Ins ...

  2. 场景实践:基于 IntelliJ IDEA 插件部署微服务应用

    体验简介 阿里云云起实验室提供相关实验资源,点击前往 本场景指导您把微服务应用部署到 SAE 平台: 登陆 SAE 控制台,基于 jar 包创建应用 基于 IntelliJ IDEA 插件更新 SAE ...

  3. Crane-scheduler:基于真实负载进行调度

    作者 邱天,腾讯云高级工程师,负责腾讯云 TKE 动态调度器与重调度器产品. 背景 原生 kubernetes 调度器只能基于资源的 resource request 进行调度,然而 Pod 的真实资 ...

  4. MTK 虚拟 sensor bring up (pick up) sensor2.0

    pick up bring up sensor2.0 1.SCP侧的配置 (1) 放置驱动pickup.c (2) 添加底层驱动文件编译开关 (3) 加入编译文件 (4) 增加数据上报方式 (5)修改 ...

  5. 0-1knapsack

    Python 实现0-1背包问题(回溯法) 题目 解题思路 回溯法:为了避免生成那些不可能产生最佳解的问题状态,要不断地利用限界函数(bounding function)来处死那些实际上不可能产生所需 ...

  6. 阿里云FTP服务配置

    阿里云的CENTOS 7.4 并没有开启防火墙服务 所以好多人配置了FTP后会出现各种不能访问的问题 关键原因在于端口没有开放.设置端口阿里云ECS的管理控制台中"安全组" &qu ...

  7. C#取消正在运行的Task

    更新记录 本文迁移自Panda666原博客,原发布时间:2021年6月29日. 一.说明 Task默认就依托于底层线程池中的线程,使用.NET为我们定义好的CancellationTokenSourc ...

  8. nginx https证书配置

    1. Nginx配置 server { listen 443; #指定ssl监听端口 server_name www.example.com; ssl on; #开启ssl支持 ssl_certifi ...

  9. 【.NET 6】多线程的几种打开方式和代码演示

    前言: 多线程无处不在,平常的开发过程中,应该算是最常用的基础技术之一了.以下通过Thread.ThreadPool.再到Task.Parallel.线程锁.线程取消等方面,一步步进行演示多线程的一些 ...

  10. React技巧之检查元素是否可见

    原文链接:https://bobbyhadz.com/blog/react-check-if-element-in-viewport 作者:Borislav Hadzhiev 正文从这开始~ 总览 在 ...