最近迁机房,Mysql集群换到新环境出现主从不一致的情况,具体为1236报错,集群架构采用的是双主+keepalived高可用,测试了几种恢复的方法,记录一下。
Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'Slave has more GTIDs than the master has, using the master's SERVER_UUID. This may indicate that the end of the binary log was truncated or that the last binary log file was lost, e.g., after a power or disk failure when sync_binlog != 1. The master may or may not have rolled back transactions that were already replica'
Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'could not find next log; the first event '' at 4, the last event read from './slave-bin.000135' at 234, the last byte read from './slave-bin.000135' at 234.'
先回忆下Mysql主从同步:
主从复制中分为「主服务器(master)「和」从服务器(slave)」,「主服务器负责写,而从服务器负责读」,MySQL的主从复制的过程是一个「异步的过程」。这样读写分离的过程,能够使整体的服务性能大大提升,即使写操作时间比较长,也不影响读操作的进行。
MySQL的主从复制中主要有三个线程:master(binlog dump thread)、slave(I/O thread 、SQL thread),Master一条线程和Slave中的两条线程。
主从复制大致步骤如下:
在从库上执行 change master to,将主库的信息保存到从库中的 master.info 文件中,再执行 start slave,开启 I/O Thread,SQL Thread,主从复制开始
1. I/O Thread 通过 master.info 文件中主库的连接信息去连接主库,连接成功后主库就会开启 Dump Thread
2. Dump Thread 读取主库新产生的二进制日志,然后投递给 I/O Thread
3. I/O Thread 接收 Dump Thread 投递的新的二进制日志,将日志写入到中继日志 relay log 中,I/O Thread 就会等待主库 Dump Thread 主动把新产生的二进制日志投递
4. SQL Thread 会将 relay log 新产生的日志恢复到数据库,写到磁盘
Mysql < 5.7 dump_thread 读binlog与主库写binlog是串行的操作,5.7+ dump_thread读与写binlog是并行的,主从延迟高可以配置并行复制和增强半同步复制
MySQL半同步复制是介于异步和全同步之间,主库只需要等待至少一个从节点,收到并且flush binlog到relay log文件即可。主库不需要等待所有从库给主库反馈,主库只需要收到任意一个从库反馈,而且并不是从库已经完成并提交的反馈,而是从库只用完成io_thread内容即可反馈,无需等到sql_thread的执行完成。
常用命令:
start slave; #开启io_thread和sql_thread
start slave io_thread;
start slave sql_thread;
stop slave; #关闭io_thread和sql_thread
stop slave io_thread;
stop slave sql_thread;
reset slave; #删除master.info,relay-log.info数据;删除所有relay log;将延迟选项master_delay设为0
reset master; #删除所有二进制日志文件
show master status; #查看主库状态
show global variables like '%gtid%';
show binary logs;
show processlist;
show binlog events;
从库状态详解:
show slave status \G; #查看从库状态
mysql> SHOW SLAVE STATUS\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 172.16.11.22 #主库IP Master_User: repl #主库用户 Master_Port: 3306 #主库端口号 Connect_Retry: 60 Master_Log_File: master-bin.000002 #当前从库io_thread正在读取主库的二进制日志文件 Read_Master_Log_Pos: 775058446 #当前从库io_thread正在读取主库二进制文件的位置 Relay_Log_File: relay.000004 #当前从库sql_thread正在读取从库的中继日志文件 Relay_Log_Pos: 407 #当前sql_thread正在读取从库中继日志文件的位置 Relay_Master_Log_File: master-bin.000002 #当前从库sql_thread从relay log中读取的正在进行的sql语句,对应主库的sql语句是在哪个binlog中 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: 775058446 #从库sql_thread当前执行的事件,对应主库的binlog中的position Relay_Log_Space: 691 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: 0 #主从复制延迟的时间(s),如果是0表示主从无延迟 Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 #最后一次IO线程错误信息 Last_IO_Error: Last_SQL_Errno: 0 #最后一次SQL线程错误信息 Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: 2 Master_UUID: a67d2e10-b10c-11ed-948f-fa163e521e8a Master_Info_File: mysql.slave_master_info SQL_Delay: 0 #延迟复制 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates Master_Retry_Count: 86400 Master_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: a67d2e10-b10c-11ed-948f-fa163e521e8a:6-11 #当前io_thread已经接受到的binlog Executed_Gtid_Set: a66803a8-b10c-11ed-be14-fa163e8dddf7:1-2410271, #当前从库sql_thread执行的二进制日志位置 a67d2e10-b10c-11ed-948f-fa163e521e8a:6-11 Auto_Position: 0 Replicate_Rewrite_DB: Channel_Name: Master_TLS_Version: 1 row in set (0.00 sec)
主从搭建详见:
双主搭建:
1.双主Master ⇌ Standby,my.cnf配置文件中 server-id 设置要不同,配置上log_slave_updates
2.auto_increment_offset、auto_increment_increment差开,防止两个节点双写时出现主键冲突。
auto_increment_offset 自增字段起始值,设置为 2 表示自增字段从 2 开始算
auto_increment_increment 自增字段步增数值,设置为 2 表示每次递增 2,根据主节点设置,自增字段的值可能是这样的: 1,3,5,7...
log_slave_updates 将复制事件写入 binlog, 一台服务器既做主库又做从库此选项必须要开启, 这里在 Master 和 Standby 开启
从库开启log-bin参数,如果直接往从库写数据,是可以记录log-bin日志的,但是从库通过I0线程读取主库二进制日志文件,然后通过SQL线程写入的数据,是不会记录binlog日志的。也就是说从库从主库上复制的数据,是不写入从库的binlog日志的。所以从库做为其他从库的主库时需要在配置文件中添加log-slave-updates参数。
双主配置参考:
1236报错修复测试结果:
1.standby执行reset slave,master上没有执行reset slave 主从同步不报错,master同步到standby没有问题,standby写入有时不会同步到master,两边数据会有可能造成不一致 stop slave; CHANGE MASTER TO MASTER_LOG_FILE='slave-bin.000001',MASTER_LOG_POS=34978,master_auto_position=0; start slave; 2.standby和master都执行reset slave master,standby两边写入都能正常同步,两边写入数据没有看到不一致的情况 stop slave; reset slave; CHANGE MASTER TO MASTER_LOG_FILE='slave-bin.000001',MASTER_LOG_POS=126974,master_auto_position=0; start slave; 3.standby执行reset slave & reset master(这种情况是从库的binlog已经无效了,所以要执行这个命令清空binlog,在从库执行), master 执行reset slave master,standby两边写入都能正常同步,两边写入数据没有看到不一致的情况 4.standby和master都执行reset slave & reset master,再change master(危险操作,线上慎用可能丢数据) master,standby两边写入都能正常同步,两边写入数据没有看到不一致的情况
生产建议:
1. 备节点最好动态设置只读(set global read_only = 0|1,主节点 read_only=0,备节点 read_only=1),Keepalived设置为非抢占模式,切换时,备节点要等待主节点同步完成。
2. 迁移时停服务最好是先停Standby,再停Master;启动先启Master,再启Standby。
测试数据:
Master执行: create database mytest; use mytest; CREATE TABLE `test1` ( `id` int(10) NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` varchar(125) NOT NULL, `age` int(3) DEFAULT NULL ); insert into test1 values (0, 'a', 16); insert into test1 values (0, 'b', 24); insert into test1 values (0, 'c', 18); mysql> SHOW TABLES; +------------------+ | Tables_in_mytest | +------------------+ | test1 | +------------------+ 1 row in set (0.00 sec) mysql> insert into test1 values (0, 'a', 16); Query OK, 1 row affected (0.02 sec) mysql> insert into test1 values (0, 'b', 24); Query OK, 1 row affected (0.01 sec) mysql> insert into test1 values (0, 'c', 18); Query OK, 1 row affected (0.01 sec) mysql> select * from test1; +----+------+------+ | id | name | age | +----+------+------+ | 1 | a | 16 | | 3 | b | 24 | | 5 | c | 18 | +----+------+------+ 3 rows in set (0.00 sec) Standby执行: insert into test1 values (0, 'e', 16); insert into test1 values (0, 'f', 24); insert into test1 values (0, 'g', 18); mysql> select * from test1; +----+------+------+ | id | name | age | +----+------+------+ | 1 | a | 16 | | 3 | b | 24 | | 5 | c | 18 | +----+------+------+ 3 rows in set (0.01 sec) mysql> insert into test1 values (0, 'e', 16); Query OK, 1 row affected (0.01 sec) mysql> insert into test1 values (0, 'f', 24); Query OK, 1 row affected (0.02 sec) mysql> insert into test1 values (0, 'g', 18); Query OK, 1 row affected (0.01 sec) mysql> select * from test1; +----+------+------+ | id | name | age | +----+------+------+ | 1 | a | 16 | | 3 | b | 24 | | 5 | c | 18 | | 6 | e | 16 | | 8 | f | 24 | | 10 | g | 18 | +----+------+------+ 6 rows in set (0.00 sec)
参考: