收到主从同步异常报警后查看如下异常:
Slave_SQL_Running: No
Replicate_Do_DB:
Replicate_Ignore_DB: mysql,test
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 1677
Last_Error: Column 0 of table 'sbtest.file_node_1' cannot be converted from type 'varchar(96)' to type 'varchar(32)'
Skip_Counter: 0
Exec_Master_Log_Pos: 39058460
Relay_Log_Space: 38194339
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: NULL
看到这个报错时,第一映印像是某人在从库操作了,导致表结构不一样了,字段长度设置的不一样,但通过对比两边的表结构看并非如此,长度是一样的。如下是主从的表结构
主库表结构:
mysql> show create table sbtest.file_node_1\G
*************************** 1. row ***************************
Table: file_node_1
Create Table: CREATE TABLE `file_node_1` (
`fileId` varchar(32) NOT NULL COMMENT '文件ID',
`nodeId` smallint(6) NOT NULL COMMENT '节点ID',
`cdn3Preloaded` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否已预分发。0否,1是'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
从库表结构:
root 12:54: [sbtest]> show create table sbtest.file_node_1\G
*************************** 1. row ***************************
Table: file_node_1
Create Table: CREATE TABLE `file_node_1` (
`fileId` varchar(32) NOT NULL COMMENT '文件ID',
`nodeId` smallint(6) NOT NULL COMMENT '节点ID',
`cdn3Preloaded` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否已预分发。0否,1是'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)
看下异常点的binlog
#191101 12:47:36 server id 1001126 end_log_pos 39058460 Query thread_id=526206949 exec_time=0 error_code=0
use `sbtest`/*!*/;
SET TIMESTAMP=1572583656/*!*/;
CREATE TABLE `file_node_1` (
`fileId` varchar(32) NOT NULL COMMENT '文件ID',
`nodeId` smallint(6) NOT NULL COMMENT '节点ID',
`cdn3Preloaded` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否已预分发。0否,1是'
)
/*!*/;
# at 39058460
# at 39058522
#191101 12:47:36 server id 1001126 end_log_pos 39058522 Table_map: `sbtest`. `file_node_1` mapped to number 5928099
#191101 12:47:36 server id 1001126 end_log_pos 39058588 Write_rows: table id 5928099 flags: STMT_END_F
BINLOG '
6Li7XROmRg8APgAAAFr8UwIAAKN0WgAAAAEACHRpdGFuQ0ROAA92b2RfZmlsZV9ub2RlXzEAAw8C
AQJgAAA=
6Li7XRemRg8AQgAAAJz8UwIAAKN0WgAAAAEAA//4IDAwMDAwMTFBMEYwNkNDNDk1RDE2QTM5QkVC
MTU2ODk5fQMA
'/*!*/;
### INSERT INTO `sbtest`.`file_node_1`
### SET
### @1='0000011A0F06CC495D16A39BEB156899' /* VARSTRING(96) meta=96 nullable=0 is_null=0 */
### @2=893 /* SHORTINT meta=0 nullable=0 is_null=0 */
### @3=0 /* TINYINT meta=0 nullable=0 is_null=0 */
# at 39058588
#191101 12:47:36 server id 1001126 end_log_pos 39058615 Xid = 68157541470
COMMIT/*!*/;
创建的表确实是varchar(32)的,但后面的insert into为什么就是VARSTRING(96)了呢?
其实在看表结构的时候,并没有发现明显异常点。感觉很奇怪,就算将从的字段改成varchar(96)也是无法正常同步的。与是就找操作的同学确认操作。
create table file_node_1 select * from file_node limit 1;
rename table file_node to file_node_20191101;
rename table file_node_1 to file_node;
感觉很不舒服,create table select * from xx的方式创建一张表,这种方式很明显,没法把索引copy过来。但为什么会导致同步异常呢?除了索引不会创建外,还有其它问题吗?于是重新仔细看了两边的表结构,发现了表的字符集不一样。
主库是utf8,从库是utf8mb4,为什么不一样呢,原因就很容易确认了,主从数据库的默认字集集不一样。再回头看binlog发现通过create table select * from table的方式创建:
1、create table没有带索引
2、create table没有指定字符集
3、create table没有了comment
4、create table没有了ROW_FORMAT
这样为什么说,主从两个表的字符集不一样了,默认没有指定字符集,从mysql实例的默认字符集继承。
再看insert into的binlog为什么是VARSTRING(96) ?
因为这里算上了字符集,也就是32*3=96
确认问题后,修复就比较简单,由于是刚创建表,还没有数据,直接将从的表drop掉后,再copy主库的建表语句创建表,再恢复同步。
总结:
1、复制表结构要使用create table table1 liek table2;的方式,保证表结构信息都正常复制过来
2、mysql部署时字符集不一样,这是一大坑
3、权限开得太大,建议DDL不要开放给开发,变更操作统一走变更平台,评审后再上线。这里额外提下,通过Inception评审alter rename操作第三个sql会提示表存在,无法通过评审。这个需要优化。
文章最后更新时间:
2019年11月01日 15:01:44