翼度科技»论坛 编程开发 mysql 查看内容

故障解析丨导入字符串NULL导致主从报错

7

主题

7

帖子

21

积分

新手上路

Rank: 1

积分
21
1.背景概述

目前需要搭建一个从库,由于单表数据量较大,时间比较有限,考虑到导入导出的时间,并且GreatSQL支持并行load data的功能,能够加速数据的导入,因此决定使用 select into outfile 和 load data 的方式进行数据的迁移;
在数据导入完成后进行数据同步,从库发生报错 1032 找不到记录,但是登录从库中可以查询到此条记录,这里就很奇怪;
最后通过解析relaylog,根据relaylog中的update记录,以每个字段为查询条件进行查询,发现是由于NULL值列导致的,主库这列的值是 NULL,从库在导入后导成了字符串"NULL",因此导致回放update操作时匹配不到数据而报错1032.
2.问题复现

本次测试基于 GreatSQL 8.0.32-24
2.1 初始化2个单机实例


2.2 主节点创建测试表
  1. greatsql> create database test;
  2. greatsql> use test;
  3. greatsql> create table t1 (id int,
  4. name varchar(30),
  5. age int,
  6. addr varchar(30),
  7. school varchar(30),
  8. unique key (id)) engine=innodb;
  9. greatsql> insert into t1 values
  10. (1,'小红',10,'北京','一中'),
  11. (2,'小绿',11,'北京','一中'),
  12. (3,'小黄',12,'北京',NULL),
  13. (4,'小蓝',13,'北京',NULL),
  14. (5,'小黑',14,'北京',NULL);
复制代码
2.3 查看数据
  1. greatsql> select * from t1;
  2. +----+--------+------+--------+--------+
  3. | id | name  | age  | addr  | school |
  4. +----+--------+------+--------+--------+
  5. |  1 | 小红  |  10 | 北京  | 一中  |
  6. |  2 | 小绿  |  11 | 北京  | 一中  |
  7. |  3 | 小黄  |  12 | 北京  | NULL  |
  8. |  4 | 小蓝  |  13 | 北京  | NULL  |
  9. |  5 | 小黑  |  14 | 北京  | NULL  |
  10. +----+--------+------+--------+--------+
  11. 5 rows in set (0.00 sec)
复制代码
2.4 主节点导出数据
  1. greatsql> select * from test.t1 into outfile '/greatsql/t1.csv' FIELDS TERMINATED BY '|+|'  ESCAPED BY '' LINES TERMINATED BY '/*rowsxxx*/';
复制代码
2.5 查看导出的数据
  1. $ cat t1.csv
  2. 1|+|小红|+|10|+|北京|+|一中/*rowsxxx*/2|+|小绿|+|11|+|北京|+|一中/*rowsxxx*/3|+|小黄|+|12|+|北京|+|NULL/*rowsxxx*/4|+|小蓝|+|13|+|北京|+|NULL/*rowsxxx*/5|+|小黑|+|14|+|北京|+|NULL/*rowsxxx
复制代码
可以看到导出的数据中包含 NULL
2.6 从库创建表并导入数据
  1. greatsql> create database test;
  2. use test;
  3. create table t1 (id int,
  4. name varchar(30),
  5. age int,
  6. addr varchar(30),
  7. school varchar(30),
  8. unique key (id)) engine=innodb;
复制代码
导入数据
  1. greatsql> load data  infile '/greatsql/t1.csv' into table test.t1 fields terminated by '|+|' ESCAPED BY '' lines terminated by '/*rowsxxx*/';
复制代码
2.7 从库查询数据
  1. greatsql> select * from test.t1;
  2. +----+--------+------+--------+--------+
  3. | id | name  | age  | addr  | school |
  4. +----+--------+------+--------+--------+
  5. |  1 | 小红  |  10 | 北京  | 一中  |
  6. |  2 | 小绿  |  11 | 北京  | 一中  |
  7. |  3 | 小黄  |  12 | 北京  | NULL  |
  8. |  4 | 小蓝  |  13 | 北京  | NULL  |
  9. |  5 | 小黑  |  14 | 北京  | NULL  |
  10. +----+--------+------+--------+--------+
  11. 5 rows in set (0.00 sec)
复制代码
2.8 从库建立复制
  1. greatsql> reset master;
  2. Query OK, 0 rows affected (0.04 sec)
  3. greatsql> set global gtid_purged='b94e6517-68dd-11ee-b43b-00163ecb92e3:1-5755';
  4. Query OK, 0 rows affected (0.00 sec)
  5. greatsql> show master status;
  6. +---------------+----------+--------------+------------------+---------------------------------------------+
  7. | File      | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set              |
  8. +---------------+----------+--------------+------------------+---------------------------------------------+
  9. | binlog.000001 |    153 |        |          | b94e6517-68dd-11ee-b43b-00163ecb92e3:1-5755 |
  10. +---------------+----------+--------------+------------------+---------------------------------------------+
  11. 1 row in set (0.00 sec)
  12. greatsql> change master to master_user='root',master_password='greatdb',master_host='192.168.137.162',master_port=6001,master_auto_position=1;
  13. Query OK, 0 rows affected, 7 warnings (0.02 sec)
  14. greatsql> start slave;
  15. Query OK, 0 rows affected, 1 warning (0.04 sec)
  16. greatsql> show slave status\G
  17. *************************** 1. row ***************************
  18.         Slave_IO_State: Waiting for source to send event
  19.          Master_Host: 192.168.137.162
  20.          Master_User: root
  21.          Master_Port: 6001
  22.         Connect_Retry: 60
  23.        Master_Log_File: binlog.000002
  24.      Read_Master_Log_Pos: 1861574
  25.         Relay_Log_File: relaylog.000002
  26.         Relay_Log_Pos: 395
  27.     Relay_Master_Log_File: binlog.000002
  28.        Slave_IO_Running: Yes
  29.       Slave_SQL_Running: Yes
  30.        Replicate_Do_DB:
  31. ......
复制代码
2.9 主库更新数据
  1. greatsql> update test.t1 set name='小小黑' where id=5;
复制代码
2.10 从库查看复制状态
  1. greatsql> show slave status\G
  2. *************************** 1. row ***************************
  3.         Slave_IO_State: Waiting for source to send event
  4.          Master_Host: 172.17.137.162
  5.          Master_User: root
  6.          Master_Port: 6001
  7.         Connect_Retry: 60
  8.        Master_Log_File: binlog.000002
  9.      Read_Master_Log_Pos: 1863564
  10.         Relay_Log_File: relaylog.000002
  11.         Relay_Log_Pos: 395
  12.     Relay_Master_Log_File: binlog.000002
  13.        Slave_IO_Running: Yes
  14.       Slave_SQL_Running: No
  15.        Replicate_Do_DB:
  16.      Replicate_Ignore_DB:
  17.       Replicate_Do_Table:
  18.     Replicate_Ignore_Table:
  19.    Replicate_Wild_Do_Table:
  20. Replicate_Wild_Ignore_Table:
  21.           Last_Errno: 1032
  22.           Last_Error: Coordinator stopped because there were error(s) in the worker(s). The most recent failure being: Worker 1 failed executing transaction 'b94e6517-68dd-11ee-b43b-00163ecb92e3:5756' at master log binlog.000002, end_log_pos 1863537. See error log and/or performance_schema.replication_applier_status_by_worker table for more details about this failure or others, if any.
  23.          Skip_Counter: 0
  24. ......
  25. greatsql> select * from performance_schema.replication_applier_status_by_worker limit 1\G
  26. *************************** 1. row ***************************
  27.                       CHANNEL_NAME:
  28.                        WORKER_ID: 1
  29.                        THREAD_ID: NULL
  30.                      SERVICE_STATE: OFF
  31.                    LAST_ERROR_NUMBER: 1032
  32.                    LAST_ERROR_MESSAGE: Worker 1 failed executing transaction 'b94e6517-68dd-11ee-b43b-00163ecb92e3:5756' at master log binlog.000002, end_log_pos 1863537; Could not execute Update_rows event on table test.t1; Can't find record in 't1', Error_code: 1032; Can't find record in 't1', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event's master log FIRST, end_log_pos 1863537
  33.                   LAST_ERROR_TIMESTAMP: 2023-10-17 10:02:46.396166
  34.                 LAST_APPLIED_TRANSACTION:
  35.    LAST_APPLIED_TRANSACTION_ORIGINAL_COMMIT_TIMESTAMP: 0000-00-00 00:00:00.000000
  36.   LAST_APPLIED_TRANSACTION_IMMEDIATE_COMMIT_TIMESTAMP: 0000-00-00 00:00:00.000000
  37.      LAST_APPLIED_TRANSACTION_START_APPLY_TIMESTAMP: 0000-00-00 00:00:00.000000
  38.       LAST_APPLIED_TRANSACTION_END_APPLY_TIMESTAMP: 0000-00-00 00:00:00.000000
  39.                   APPLYING_TRANSACTION: b94e6517-68dd-11ee-b43b-00163ecb92e3:5756
  40.      APPLYING_TRANSACTION_ORIGINAL_COMMIT_TIMESTAMP: 2023-10-17 10:02:46.392331
  41.     APPLYING_TRANSACTION_IMMEDIATE_COMMIT_TIMESTAMP: 2023-10-17 10:02:46.392331
  42.        APPLYING_TRANSACTION_START_APPLY_TIMESTAMP: 2023-10-17 10:02:46.393814
  43.          LAST_APPLIED_TRANSACTION_RETRIES_COUNT: 0
  44.   LAST_APPLIED_TRANSACTION_LAST_TRANSIENT_ERROR_NUMBER: 0
  45. LAST_APPLIED_TRANSACTION_LAST_TRANSIENT_ERROR_MESSAGE:
  46. LAST_APPLIED_TRANSACTION_LAST_TRANSIENT_ERROR_TIMESTAMP: 0000-00-00 00:00:00.000000
  47.            APPLYING_TRANSACTION_RETRIES_COUNT: 0
  48.     APPLYING_TRANSACTION_LAST_TRANSIENT_ERROR_NUMBER: 0
  49.    APPLYING_TRANSACTION_LAST_TRANSIENT_ERROR_MESSAGE:
  50.   APPLYING_TRANSACTION_LAST_TRANSIENT_ERROR_TIMESTAMP: 0000-00-00 00:00:00.000000
  51. 1 row in set (0.00 sec)
复制代码
可以看到从库发生1032报错,找不到记录
2.11 解析从库relay log
  1. #231017 10:02:46 server id 1  end_log_pos 1863456 Table_map: `test`.`t1` mapped to number 180
  2. \# has_generated_invisible_primary_key=0
  3. \# at 673
  4. \#231017 10:02:46 server id 1  end_log_pos 1863537 Update_rows: table id 180 flags: STMT_END_F
  5. \### UPDATE `test`.`t1`
  6. \### WHERE
  7. \###  @1=5 /* INT meta=0 nullable=1 is_null=0 */
  8. \###  @2='小黑' /* VARSTRING(120) meta=120 nullable=1 is_null=0 */
  9. \###  @3=14 /* INT meta=0 nullable=1 is_null=0 */
  10. \###  @4='北京' /* VARSTRING(120) meta=120 nullable=1 is_null=0 */
  11. \###  @5=NULL /* VARSTRING(120) meta=120 nullable=1 is_null=1 */
  12. \### SET
  13. \###  @1=5 /* INT meta=0 nullable=1 is_null=0 */
  14. \###  @2='小小黑' /* VARSTRING(120) meta=120 nullable=1 is_null=0 */
  15. \###  @3=14 /* INT meta=0 nullable=1 is_null=0 */
  16. \###  @4='北京' /* VARSTRING(120) meta=120 nullable=1 is_null=0 */
  17. \###  @5=NULL /* VARSTRING(120) meta=120 nullable=1 is_null=1 */
  18. \# at 754
  19. \#231017 10:02:46 server id 1  end_log_pos 1863564 Xid = 5940
  20. COMMIT/*!*/;
  21. SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by greatsqlbinlog */ /*!*/;
  22. DELIMITER ;
  23. \# End of log file
复制代码

可以看到update更新前的数据与从库的数据一致,那么为什么还会报错 找不到记录呢?
2.12 根据relay log中的内容去从库查询数据
  1. greatsql> select * from test.t1 where id=5;
  2. +------+--------+------+--------+--------+
  3. | id  | name  | age  | addr  | school |
  4. +------+--------+------+--------+--------+
  5. |   5 | 小黑  |  14 | 北京  | NULL  |
  6. +------+--------+------+--------+--------+
  7. 1 row in set (0.01 sec)
  8. greatsql> select * from test.t1 where name='小黑';
  9. +------+--------+------+--------+--------+
  10. | id  | name  | age  | addr  | school |
  11. +------+--------+------+--------+--------+
  12. |   5 | 小黑  |  14 | 北京  | NULL  |
  13. +------+--------+------+--------+--------+
  14. 1 row in set (0.01 sec)
  15. greatsql> select * from test.t1 where age=14;
  16. +------+--------+------+--------+--------+
  17. | id  | name  | age  | addr  | school |
  18. +------+--------+------+--------+--------+
  19. |   5 | 小黑  |  14 | 北京  | NULL  |
  20. +------+--------+------+--------+--------+
  21. 1 row in set (0.00 sec)
  22. greatsql> select * from test.t1 where addr='北京';
  23. +------+--------+------+--------+--------+
  24. | id  | name  | age  | addr  | school |
  25. +------+--------+------+--------+--------+
  26. |   1 | 小红  |  10 | 北京  | 一中  |
  27. |   2 | 小绿  |  11 | 北京  | 一中  |
  28. |   3 | 小黄  |  12 | 北京  | NULL  |
  29. |   4 | 小蓝  |  13 | 北京  | NULL  |
  30. |   5 | 小黑  |  14 | 北京  | NULL  |
  31. +------+--------+------+--------+--------+
  32. 5 rows in set (0.00 sec)
  33. greatsql> select * from test.t1 where school is null;
  34. Empty set (0.01 sec)
  35. greatsql> select * from test.t1 where school='null';
  36. +------+--------+------+--------+--------+
  37. | id  | name  | age  | addr  | school |
  38. +------+--------+------+--------+--------+
  39. |   3 | 小黄  |  12 | 北京  | NULL  |
  40. |   4 | 小蓝  |  13 | 北京  | NULL  |
  41. |   5 | 小黑  |  14 | 北京  | NULL  |
  42. +------+--------+------+--------+--------+
  43. 3 rows in set (0.00 sec)
复制代码
可以看到,根据null值作为查询条件时,匹配不到数据; 根据字符串"null" 进行匹配是可以匹配到数据
2.13 去主库进行查询
  1. greatsql> select * from test.t1 where school is null;
  2. +------+-----------+------+--------+--------+
  3. | id  | name    | age  | addr  | school |
  4. +------+-----------+------+--------+--------+
  5. |   3 | 小黄    |  12 | 北京  | NULL  |
  6. |   4 | 小蓝    |  13 | 北京  | NULL  |
  7. |   5 | 小小黑   |  14 | 北京  | NULL  |
  8. +------+-----------+------+--------+--------+
  9. 3 rows in set (0.00 sec)
  10. greatsql> select * from test.t1 where school='null';
  11. Empty set (0.00 sec)
复制代码
在主库查询的结果与从库相反
可以得出结论,由于从库导入的数据将NULL值列的数据导入成了字符串 NULL,因此导致主从数据出现了不一致。
2.14 修复从库
  1. greatsql> stop slave;
  2. Query OK, 0 rows affected, 1 warning (0.00 sec)
  3. greatsql> update test.t1 set school=NULL where school='null';
  4. Query OK, 3 rows affected (0.01 sec)
  5. Rows matched: 3  Changed: 3  Warnings: 0
  6. greatsql> start slave;
  7. Query OK, 0 rows affected, 1 warning (0.04 sec)
  8. greatsql> show slave status\G
  9. *************************** 1. row ***************************
  10.         Slave_IO_State: Waiting for source to send event
  11.          Master_Host: 172.17.137.162
  12.          Master_User: root
  13.          Master_Port: 6001
  14.         Connect_Retry: 60
  15.        Master_Log_File: binlog.000002
  16.      Read_Master_Log_Pos: 1863564
  17.         Relay_Log_File: relaylog.000003
  18.         Relay_Log_Pos: 435
  19.     Relay_Master_Log_File: binlog.000002
  20.        Slave_IO_Running: Yes
  21.       Slave_SQL_Running: Yes
  22.        Replicate_Do_DB:
  23. ......
复制代码
可以看到主从状态已经恢复正常
3.总结

1.如果FIELDS ESCAPED BY字符为空字符,则没有字符被转义,并且NULL被作为NULL输出,而不是\N;这也是导致此次主从报错的原因。
2.如果这张表使用的是主键而不是唯一索引,即使某些列被导入为字符串NULL,也不会报错。
3.如果这张表没有索引或有普通索引,则会报错。

Enjoy GreatSQL
来源:https://www.cnblogs.com/greatsql/p/17898347.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具