技术分享 | load data导致主键丢失的神秘问题
欢迎来到 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导致主键丢失的神秘问题的更多相关文章
- (转)mysql自增列导致主键重复问题分析
mysql自增列导致主键重复问题分析... 原文:http://www.cnblogs.com/cchust/p/3914935.html 前几天开发童鞋反馈一个利用load data infile ...
- mysql自增列导致主键重复问题分析。。。
前几天开发童鞋反馈一个利用load data infile命令导入数据主键冲突的问题,分析后确定这个问题可能是mysql的一个bug,这里提出来给大家分享下.以免以后有童鞋遇到类似问题百思不得其解,难 ...
- 成都项目中因为MYSQL与SSDB备分时间不一致,导致主键产生器错误解决一例
-- JFinal错误提示 Duplicate entry '1791361-1823391' for key 'PRIMARY' -- 1.查看SSDB的主键生成器值ssdb 127.0.0.1:8 ...
- MySQL 中的自增主键
MySQL 的主键可以是自增的,那么如果在断电重启后新增的值还会延续断电前的自增值吗?自增值默认为1,那么可不可以改变呢?下面就说一下 MySQL 的自增值. 特点 保存策略 1.如果存储引擎是 My ...
- Hibernate(4)——主键生成策略、CRUD 基础API区别的总结 和 注解的使用
俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及的知识点总结如下: hibernate的主键生成策略 UUID 配置的补充:hbm2ddl.auto属性用法 注解还是配置文件 h ...
- 深入理解Redis主键失效原理及实现机制
http://blog.jobbole.com/71095/ 对于缓存失效,不同的缓存有不同的处理机制,可以说是大同中有小异,作者通过对Redis 文档与相关源码的仔细研读,为大家详细剖析了 Redi ...
- Hibernate联合主键映射
1.联合主键的映射规则 1) 类中的每个主键属性都对应到数据表中的每个主键列. Hibernate要求具有联合主键的实体类实现Serializable接口,并且重写hashCode与equals方法, ...
- 二、Sql Server 基础培训《进度2-关于主键(知识点学习)》
学习作业2: 问题1:主键都有哪些方式? 问题2:本次实战案例建立的主键采用哪种方式? 问题3:猜猜金蝶K3WISE建立的主键采用哪种方式? 问题4:谈谈手工主键增长设置具体实现思路?(选 ...
- Redis主键失效 - 原理及实现机制
[数据记录过期源码][http://blog.csdn.net/yuanrxdu/article/details/21233047] [http://blog.jobbole.com/71095/] ...
随机推荐
- 使用虚拟机在3台centos7系统安装docker和k8s集群
一.安装docker 环境:准备3台centos7系统,都安装上docker环境,具体安装步骤和流程如下 参考: https://docs.docker.com/install/linux/docke ...
- DCM:一个能够改善所有应用数据交互场景的中间件新秀
摘要:几乎所有涉及应用数据交互的场景都可以通过DCM来改善应用结构,提升开发与计算效率. 本文分享自华为云社区<DCM:中间件家族迎来新成员>,作者: 石臻臻的杂货铺. DCM是什么 现代 ...
- js 动画补间 Tween
1 /* RunningList (触发过程中可以安全的删除自己) 2 如果触发过程中删除(回调函数中删除正在遍历的数组), 不仅 len 没有变(遍历前定义的len没有变, 真实的len随之减少), ...
- UniqueMergeTree:支持实时更新删除的 ClickHouse 表引擎
UniqueMergeTree 开发的业务背景 首先,我们看一下哪些场景需要用到实时更新. 我们总结了三类场景: 第一类是业务需要对它的交易类数据进行实时分析,需要把数据流同步到 ClickHouse ...
- java基础题(4)
5.4接口和抽象类 5.4.1实现抽象方法 描述: 已知抽象类Base中定义了calculate方法,该方法的计算过程依赖于sum()和avg(),而后两个方法均为抽象方法.要求定义Base的子类Su ...
- React简单教程-4.1-hook
前言 虽然我们简单感受了一下 useState 的用法,但我想你还是对 React 里的 hook 迷迷糊糊的.本文我们将明确下 React 的概念. HOOK 前生今世 在我示例中,写的 React ...
- c++ 线段树
关于线段树 线段数是一种区间树 可以看出:叶子即为输入的数 假设一个节点为 x ,则其左儿子为 2x 右儿子为 2x+1 操作解析 约定 变量名 意义 input[] 输入的数 t[] 线段树 其中 ...
- python爬虫之JS逆向某易云音乐
Python爬虫之JS逆向采集某易云音乐网站 在获取音乐的详情信息时,遇到请求参数全为加密的情况,现解解决方案整理如下: JS逆向有两种思路: 一种是整理出js文件在Python中直接使用execjs ...
- Jmeter(五十四) - 从入门到精通高级篇 - 如何在linux系统下运行jmeter脚本 - 上篇(详解教程)
1.简介 上一篇宏哥已经介绍了如何在Linux系统中安装Jmeter,想必各位小伙伴都已经在Linux服务器或者虚拟机上已经实践并且都已经成功安装好了,那么今天宏哥就来介绍一下如何在Linux系统下运 ...
- springboot available: expected at least 1 bean which qualifies as autowire candidate奇葩问题
Exception encountered during context initialization - cancelling refresh attempt: org.springframewor ...