背景描述 案件回顾
某著名保险企业邮件一条DTS链路故障,报错信息显示目标库的对应表不存在
链路是整库同步,支持源库创建表后,目标库会同步创建表
请我方协助排查原因并修复故障
先介绍一下数据表情况,因为涉及到内部真实的数据,所以表结构和日志都做了脱敏操作,但不会影响具体的分析。
信息详情
信息
内容
故障时间点
2020-05-20 10:33:50
DTS链路名称
Polardb 5.6->RDS for MySQL5.7
DTS同步失败报错内容
DTS-071001: the Table cds.plog Information Loaded failed in Target DB. cause by [com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:Table ‘cds.plog’ doesn’t exist] About more information in [https://yq.aliyun.com/articles/505378] .
同步粒度
整库同步
源数据库名称
PDB
源数据库类型
PolarDB for MySQL 5.6
源库同步用户/权限
i_read/只读
目标数据库名称
RDS
目标数据库类型
RDS for MySQL 5.7
目标库同步用户/权限
e_dev/读写
排障过程 阶段一:怀疑人为因素造成
解析故障时间段源目标库的 binlog 来验证
下载目标数据库对应时间段的binlog → mysql-bin.000498,并通过mysqlbinlog -vv 的方式对日志进行解析
目标库 binlog 解析结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 COMMIT ;SET @@SESSION.GTID_NEXT= '9669d21f-7e11-11ea-bdd0-6c92bf5c9bf2:6704774' ;use `cloudods` ;SET TIMESTAMP =1589941811 ;SET @@session.foreign_key_checks=0 ;SET @@session.sql_mode=0 ;; SET @@session.character_set_client=45 ,@@session.collation_connection=45 ,@@session.collation_server=33 ;DROP TABLE IF EXISTS `plog` ; ; SET @@SESSION.GTID_NEXT= '9669d21f-7e11-11ea-bdd0-6c92bf5c9bf2:6704775' ;SET TIMESTAMP =1589941820 ;BEGIN ; BINLOG ' PJbEXhPpg8yHqgAAAOQ9iQAAAFIFAAAAAAEADGlzcF9tYWluZGJfcwAMc3lzX3VzZXJpbmZvACoI Dw8PDw8PDw8PDw8SDw8DAwMDDxISCPwSAw8PEhIPDw8SEg8PDw8PDw89YAADAAMA7gIwADAANgAY AFgCYABgAAADAIABAwAAAAIAlgBgAAAA/QJYAlgCAAAeAP0C/QL9Av0CPAADAP7/////AwRlIl0= PJbEXhnpg8yHaQIAAE1AiQAAAFIFAAAAAAMAKv///////8BYAACA/LEIAAAAAAAACTAwMDAxNjk3 MQFBATEhAOacseWLpOWNjiAgICAgICAgICAgICAgICAgICAgICAgIAROVUxMBDA5ODYEAEZFMDEJ RzAwMDAwNDA5ATABAAAAAQAAAAEAAAABAAAAAUGZpG4AAJmknQSyAAAAAAAAAADKAHtBU0lUVTpD TSxFQUZDUjowMDAwMDM0MjEsQ0hBTk5FTDpBZ2VuY3l9LXsiQUdUQ09EIjoiMDAwMDE2OTcxICAg ICIsIk9GRkNPRCI6IkZFMDEiLCJBR1lDT0QiOiJHMDAwMDA0MDkiLCJyb3dfbnVtYmVyIjo5NSwi Q09OVFlQRSI6Ik0xIiwiaW5pUHdkIjoiMTExMTExMTEiLCJBR1ROQU1FIjoi5pyx5Yuk5Y2OICAg ICAgICAgICAgICAgICAgICAgICAgIn2ZpgcaagAAAAAGQWdlbmN5CDExMDMwNTM3mZ3lBKiZpmil lAIAQ00IADExMTExMTExGABSWU96dm9Vd3JiaEJBeWwxNnhxLzRRPT2ZpmimRpmmaKZGA0lTUEwA MDdFNDAwMDUwMDE0MDAwQTAwMTYwMDE2MEI4MEYyNDAwMDAwMDAxRDAwMDBBRkI0MDAwMDM0MUEw MDA2MDAwMDAwMDEwMDAwMDAwMCIAOTIwMDAwMDA0MzQ4NEY1NDMzNTIzODQzNTM1MTRDMzEzMRQA MDAwMEFGQjQwMDAwMzQxQTAwMDYUMjAyMDA1MjAxMDIyMjIxOTMwMDABSe7KpkM= ';
发现SQL:DROP TABLE IF EXISTS plog
/* generated by server */
执行时间:200520 10:30:11
由于该DROP动作,无论库中是否存在该表,binlog均会记录,因此想何时该DROP动作在建表之后
从源库information_schema.tables获取表的创建时间
与DROP的时间一致
因后来证实,当 alter 表时,create_time 字段会被更新;增删改查表时,update_time字段会被更新;truncate表时,时间字段不被更新
因此怀疑该时间不够准确,决定从PolarDB获取对应时间段的binlog日志,查看该时间是否是CREATE TABLE的时间
由于PolarDB不支持直接下载binlog 需要通过客户端连接的方式把binlog日志解析到本地
通过客户端连接获取binlog的命令
1 mysqlbinlog -vv -P 3306 -uxxxx -pxxxx -hxxxx.mysql.rds.aliyuncs.com --read-from-remote-server --start-datetime='2020-05-20 09:00:00' --stop-datetime='2020-05-20 11:00:00' --base64-output=DECODE-ROWS mysql-bin.000xxx > xxxlog.sql
遇到报错的情况下,需检查mysqlbinlog版本是否正确
源端数据库binlog内容
源库故障时间 binlog 解析结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 SET @@SESSION.GTID_NEXT= 'e1ecc4a7-f9fa-11e9-ad35-506b4b478fa4:107546949' ;use `cloudods` ;SET TIMESTAMP =1589941810 ;; SET @@session.character_set_client=33 ,@@session.collation_connection=33 ,@@session.collation_server=33 ;DROP TABLE IF EXISTS `plog` ; SET @@SESSION.GTID_NEXT= 'e1ecc4a7-f9fa-11e9-ad35-506b4b478fa4:107546950' ;SET TIMESTAMP =1589941811 ;; SET @@session.character_set_client=45 ,@@session.collation_connection=45 ,@@session.collation_server=33 ;create table `cds` .`plog` (^M `co` varchar (4 ) collate utf8_bin not null default '' ,^M `polnum` varchar (10 ) collate utf8_bin not null default '' ,^M `code` varchar (5 ) collate utf8_bin default null ,^M `msg` varchar (50 ) collate utf8_bin default null ,^M `lstupdtime` datetime(6 ) default null ,^M `dtl__capxrestart1` varchar (255 ) collate utf8_bin default null ,^M `dtl__capxrestart2` varchar (255 ) collate utf8_bin default null ,^M `dtl__capxuow` varchar (255 ) collate utf8_bin default null ,^M `dtl__capxuser` varchar (255 ) collate utf8_bin default null ,^M `dtl__capxtimestamp` varchar (20 ) collate utf8_bin default null ,^M `dtl__capxaction` varchar (1 ) collate utf8_bin default null ,^M `dtl__capxcasdelind` varchar (1 ) collate utf8_bin default null ,^M `dtl__capxrrn` decimal (10 ,0 ) default null ,^M `inserttime` timestamp ,^M `updatetime` timestamp ,^M `srcsys` varchar (10 ) collate utf8_bin default null ,^M primary key (`co` ,`polnum` ,`lstupdtime` )^M ) engine =innodb default charset =utf8 collate =utf8_bin ; SET @@SESSION.GTID_NEXT= 'e1ecc4a7-f9fa-11e9-ad35-506b4b478fa4:107546951' ;SET TIMESTAMP =1589941819 ;BEGIN ;
获得如下发现:
几乎相同的时间都有DROP TABLE的动作
区别是源端有CREATE TABLE的记录,而目标端没有CREATE TABLE的记录
阶段二:怀疑DTS工具BUG
阶段三:手动建表进行故障恢复
源端 SHOW CREATE TABLE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 mysql>show create table plog\G *************************** 1. row *************************** Table: plog Create Table : CREATE TABLE `plog` ( `co` varchar (4 ) COLLATE utf8_bin NOT NULL DEFAULT '' , `polnum` varchar (10 ) COLLATE utf8_bin NOT NULL DEFAULT '' , `code` varchar (5 ) COLLATE utf8_bin DEFAULT NULL , `msg` varchar (50 ) COLLATE utf8_bin DEFAULT NULL , `lstupdtime` datetime(6 ) NOT NULL DEFAULT '0000-00-00 00:00:00.000000' , `dtl__capxrestart1` varchar (255 ) COLLATE utf8_bin DEFAULT NULL , `dtl__capxrestart2` varchar (255 ) COLLATE utf8_bin DEFAULT NULL , `dtl__capxuow` varchar (255 ) COLLATE utf8_bin DEFAULT NULL , `dtl__capxuser` varchar (255 ) COLLATE utf8_bin DEFAULT NULL , `dtl__capxtimestamp` varchar (20 ) COLLATE utf8_bin DEFAULT NULL , `dtl__capxaction` varchar (1 ) COLLATE utf8_bin DEFAULT NULL , `dtl__capxcasdelind` varchar (1 ) COLLATE utf8_bin DEFAULT NULL , `dtl__capxrrn` decimal (10 ,0 ) DEFAULT NULL , `inserttime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP , `updatetime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' , `srcsys` varchar (10 ) COLLATE utf8_bin DEFAULT NULL , PRIMARY KEY (`co` ,`polnum` ,`lstupdtime` ) ) ENGINE =InnoDB DEFAULT CHARSET =utf8 COLLATE =utf8_bin 返回行数:[1 ],耗时:6 ms.
将该建表语句复制到目标端手动创建,然后启动DTS链路
再通过SELECT * FROM 表 来验证数据是否正常同步过来时,提示该表不存在
刷新DTS链路信息,DTS链路报错,报错与之前一致
多次重复上述操作,均可复现,现象反馈给阿里方,协助排查该问题
阶段四:开启审计,查找“真凶”
在阿里方的建议下,开启审计,查看是“谁”删除了这张表
审计内容
发现是eds_dev用户,是 DTS 同步用户,传输过来的动作是先进行 DROP 再进行创建,但创建失败
CREATE 之前先 DROP,就是手动创建被会失败的原因
而 CREATE 之所以失败,根据审计的提示,失败代码:1171
查询 MySQL 的错误代码 1171
当主键的字段属性为“允许空值”时会报出1171的错误代码
检查审计 CREATE TABLE 语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 create table `cds` .`plog` ( `co` varchar (4 ) collate utf8_bin not null default '' , `polnum` varchar (10 ) collate utf8_bin not null default '' ,`code` varchar (5 ) collate utf8_bin default null ,`msg` varchar (50 ) collate utf8_bin default null ,`lstupdtime` datetime(6 ) default null ,`dtl__capxrestart1` varchar (255 ) collate utf8_bin default null ,`dtl__capxrestart2` varchar (255 ) collate utf8_bin default null ,`dtl__capxuow` varchar (255 ) collate utf8_bin default null ,`dtl__capxuser` varchar (255 ) collate utf8_bin default null ,`dtl__capxtimestamp` varchar (20 ) collate utf8_bin default null ,`dtl__capxaction` varchar (1 ) collate utf8_bin default null ,`dtl__capxcasdelind` varchar (1 ) collate utf8_bin default null ,`dtl__capxrrn` decimal (10 ,0 ) default null ,`inserttime` timestamp ,`updatetime` timestamp ,`srcsys` varchar (10 ) collate utf8_bin default null ,primary key (`co` ,`polnum` ,`lstupdtime` ) ) engine =innodb default charset =utf8 collate =utf8_bin
发现以下问题:
与之前在源库SHOW CREATE TABLE的SQL不一致
lstupdtime 字段属性有 default null,但后面又被包含在primary key中(引起 1171 报错)
与源库在 binlog 获取 CREATE TABLE 的SQL一致
产生以下推测:
SHOW CREATE TABLE的SQL是系统修正后的“正规”SQL
binlog拿到的SQL是人为输入的“原生”SQL
“原生”SQL在源库建表成功,在目标库执行失败,可能是版本差异引起的 验证版本差异:
5.6 → 成功创建
5.7 → 报错 ERROR 1171
结论
建表语句不规范,导致DTS链路故障
在MySQL 5.6 中,当设计表时 datetime 数据类型的字段带有 default null,且设置为主键时,MySQL会自动将 default null 更正为 not null default ‘0000-00-00 00:00:00’,以满足 PRIMARY KEY 的需求
在MySQL 5.7 中,当设计表时 datetime 数据类型的字段带有 default null,且设置为主键时,MySQL遵照用户的需求(default null),与 PRIMARY KEY 冲突,因此报错 1171
测试用例 —MySQL 5.6—
SQL中字段属性(并设置为主键)
结果
SHOW CREATE TABLE
col_test datetime(6)
建表成功
col_test datetime(6) NOT NULL DEFAULT ‘0000-00-00 00:00:00.000000’
col_test datetime(6) default null
建表成功
col_test datetime(6) NOT NULL DEFAULT ‘0000-00-00 00:00:00.000000’
col_test datetime(6) not null
建表成功
col_test datetime(6) NOT NULL
—MySQL 5.7—
SQL中字段属性(并设置为主键)
结果
SHOW CREATE TABLE
col_test datetime(6)
建表成功
col_test datetime(6) NOT NULL
col_test datetime(6) default null
建表失败
ERROR 1171
col_test datetime(6) not null
建表成功
col_test datetime(6) NOT NULL
知识延伸 explicit_defaults_for_timestamp 参数
该参数只限制 timestamp 数据类型,对 datetime 无法起到限制作用
MySQL 5.6 需要在配置文件中修改该参数,重启服务后生效
MySQL 5.7 可直接修改该参数
explicit_defaults_for_timestamp
SQL中字段属性
SHOW CREATE TABLE
OFF / 0
col01 timestamp col02 timestamp
col01 timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP col01 timestamp NOT NULL DEFAULT ‘0000-00-00 00:00:00’
ON / 1
col01 timestamp col02 timestamp
col01 timestamp NULL DEFAULT NULL col01 timestamp NULL DEFAULT NULL
解决方案
由于同步链路“卡死”在当前这个建表的事务
因此需要将该库从同步链路中移除,然后将整库重新加入到同步链路中
阿里方虽然可以通过后台跳过该DDL,但有风险(可能跳过不止一个DDL)
思考
如何普及研发人员操作数据库的规范?
正确的互联网发布流程怎样才可以避免此类问题?