总结:复制在特定的 binlog 和位置上停滞,我不确定原因
我有一个使用 MySQL 5.5 的 MySQL 复制设置。
此复制设置从未出现过落后的情况并且始终很稳定。
今天早上,我注意到 Slave 比 Master 落后了 17 个小时。
经过进一步研究,它看起来是 SQL_Thread 的一个问题。
根据从服务器(via SLAVE STATUS
)的信息,当前主服务器日志文件位于mysql-bin.001306
@ position 20520499
。这与主服务器的输出一致MASTER STATUS
。
然而,SLAVE STATUS
显示Relay_Master_Log_File
目前mysql-bin.001302
为。在我今早监测它们的时候,它们一点也没有Exec_Master_Log_Pos
进步。36573336
Relay_Master_Log_File
Exec_Master_Log_Pos
查看 master 上的 binlog,以下语句位于mysql-bin.001302@3657336
:
# at 36573053
#170221 14:33:48 server id 1 end_log_pos 36573130 Query thread_id=96205677 exec_time=0 error_code=0
SET TIMESTAMP=1487716428/*!*/;
BEGIN
/*!*/;
# at 36573130
# at 36573213
#170221 14:33:48 server id 1 end_log_pos 36573213 Table_map: `database-name`.`table-name` mapped to number 5873
#170221 14:33:48 server id 1 end_log_pos 36573309 Write_rows: table id 5873 flags: STMT_END_F
### INSERT INTO `database-name`.`table-name`
### SET
### @1='xxxxxxxx'
### @2=6920826
### @3='xxxxxxxx'
### @4='GET'
### @5='address'
### @6=2017-02-21 14:40:24
### @7=2017-02-21 14:40:24
# at 36573309
#170221 14:33:48 server id 1 end_log_pos 36573336 Xid = 1668637037
COMMIT/*!*/;
# at 36573336
昨天这个时候,我确实执行了一些大型查询,以将数据迁移到新表。这个过程看起来有点像这样;
mysql> insert into tmp_table ( select <rows> from origin table ); -- 44 million rows
mysql> insert into dest_table ( select * from tmp_table ); -- 44 million rows
有问题的两张表没有它们有主键或唯一键,我读到过这可能是个问题。但是,虽然上面的 binlog 条目中显示的数据库 + 表是此处的目标表 - 但显示的插入记录不是在迁移期间生成的。
如果您已经走到这一步,您就值得获得网络积分。
目前,我不确定还需要考虑什么,或者在哪里可以找到日志停滞的原因。任何见解都值得赞赏。
谢谢。
作为参考,以下是截至本文发布时的输出MASTER STATUS
:SLAVE STATUS
主人地位
mysql> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.001306 | 20520499 | | |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
奴隶地位
mysql> show slave status \G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: master-host
Master_User: replication-user
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.001306
Read_Master_Log_Pos: 20520499
Relay_Log_File: relay-bin.002601
Relay_Log_Pos: 36573482
Relay_Master_Log_File: mysql-bin.001302
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 36573336
Relay_Log_Space: 3565987462
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: 63435
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
1 row in set (0.00 sec)
答案1
从昨天开始,我进行的大量查询交易就走在了正确的轨道上。
迁移数据后,我在原始表上执行了 DELETE 语句,以删除已迁移的行。
这些表只是充满了跟踪数据,因此没有任何主键或唯一键。
由于基于 ROW 的复制的工作方式,从属服务器不会执行与主服务器相同的 DELETE 语句,而是为每一行执行一个 DELETE 语句,最终看起来像这样:
DELETE FROM table WHERE colA=foo AND colB=bar AND colC=baz....etc
并且,由于没有与该查询匹配的索引,单线程复制 SQL 线程执行了 4000 万 + 删除语句(或...正在尝试执行),这需要很长时间才能运行,因为必须进行所有扫描才能识别每一行(当时表的大小约为 8000 万行)。
最后,我通过停止从属线程(STOP SLAVE
)、跳过单个从属事务(SET GLOBAL sql_slave_skip_counter = 1;
)、然后重新启动从属线程(START SLAVE
)来解决这个问题。
这导致我的主服务器和从服务器在有问题的表上不同步 - 但我能够利用基于行的复制的性质通过在主服务器上执行以下操作使其重新同步:
mysql> CREATE TABLE table_tmp; -- with the same schema as 'table' (SHOW CREATE TABLE table;)
mysql> RENAME TABLE table TO table_bak, table_tmp TO table;
mysql> INSERT INTO table ( SELECT * FROM table_bak );
mysql> DROP TABLE table_bak;
由于 DELETE 是在主服务器上执行的,因此此处的 INSERT 仅插入了我想要保留的记录(已删除的记录已消失)。而且,由于基于行的复制会单独插入每一行,而不是执行相同的 INSERT INTO...SELECT 语句,因此从属表仅填充了所需的数据。然后,后续的 DROP TABLE 语句会删除从属服务器上的表,而无需单独处理每一行。
需要注意的是,由于主表版本仍然有 3000 万到 4000 万行...INSERT 和随之而来的复制最终会锁定你的从属服务器一小会儿(重复上述问题),但由于 mysql 不必扫描数据库以查找要删除的行,因此停顿时间要短得多(最终约为 20 分钟)。
我希望这篇文章将来能对某人有所帮助。抱歉,这篇文章有些冗长,希望它能提供信息并有所帮助。