Posts tagged ‘change master’

非常规change master

主备备的两个备机转为双master时出现的诡异slave lag问题

有三台MySQL服务器,a,b和c,复制关系为 a -> b -> c。a,b,c的server_id分别为1,2,3
因为需要切换为 a b <-> c,也就是说,a单独出来,b和c作为双master结构时。
这种切换会经常出现在需要搭建备机把数据备份出来,然后把a独立出来的case中。

昨天,我就做了这样的切换,结果发现出现莫名奇妙的slave lag。
Seconds_Behind_Master一下子为0,一下子变成几千秒。
使用mysqlbinlog查看,binlog日志里面也有很多时间在几小时以前的event数据。
为了验证复制是否正常,我特别测试了一下,在b建一个表,并插入时间数据,到c上一看,表已经复制过来了,时间数据也是正确。
询问了一下同事,他说应该是MySQL的bug,在这种切换的情况下很容易触发这个bug,可以采用stop slave;change master; start slave;的方法来修复。但是实际的数据其实完全没有影响,复制还是正常的。

于是我按照这个办法:
stop slave io_thread;
stop slave;
show slave status\G
(这里先停io_thread是为了SQL thread和IO thread都执行到了同一个位置,change master 的时候没有风险)
stop slave;change master to … ; start slave;
(change master到show slave status的Master_Log_File:和Exec_Master_Log_Pos:位置,也就是说,其实根本没有改变复制的位置)

结果slave lag依然故我。这个问题就比较郁闷了。时间已经过了午夜,脑袋也转不动了,想过不管它了,反正复制没有问题。但是问题没有解决总觉得什么东西卡在喉咙一样。各种资料,各种变量都参考了一遍,最后,基本不太意识的输入:
show master logs;
show binlog events in ‘mysql-bin.000680’ from 34385301;
想看看最新产生的event,结果就发现不对的地方了。
这个最新产生的event有很多,并且server_id是1,1是a的server_id啊,应用访问的是b啊,怎么会在b上面产生a的server_id列,MySQL哪里出问题了?

仔细一想,明白了,事情是这样的:
a -> b -> c,a的event1(server_id为1)复制到b,也会复制到c,这个是正常的。
然后搭建c -> b的复制关系时,b需要断开a的连接,切换主库到c,在 change master 的位置在event1出现之前,那么event1肯定会被重新复制到b去,event1的server_id是1,那么b判断,这个event1不是我提交的,需要在本地执行,并且把它记录到了自己的binlog中;
由于b和c是双master结构,event1又复制到了c,c同样判断它不是我提交的,那么我需要在本地执行,并且记录到本地binlog中。
这样event1就在b和c之间循环往复,时间保持不变,MySQL的slave lag也就一下子是0,一下子是几千秒了。

这里,还需要说明一点,在环型复制里面,event之所以能够在环内只循环一次,而不是重复做,是因为提交的那个节点会发现这个event的server_id是自己的server_id,也就是说是自己提交的。那么,它就不会把这个event再应用一次,自然也不会记录到binlog。这个循环就结束了。除非你闲着没事做,设置了replicate-same-server-id参数。

那么解决问题怎么办列,很简单,把没有应用访问的c的server_id设置成a的server_id:
set global server_id=1;
看看时间差不多了,server_id为1的event都被干掉以后:
set global server_id=3;
然后再设置回来。
还好,MySQL 5.0和5.1的server_id都是动态的。

may your success.

双slave的server_id相同的问题

前段时间新来了一个机器,想要替换一个应用的备机。
已有的架构是这样的:A和B两个MySQL相互作为备机,A的性能比较好,B的性能比较差。
现在买了一台新的机器,新的机器c性能比较好,准备用来替换B。
安装MySQL和配置等等都不细说,比较顺利的。

但是安装好以后,配置复制的时候出现了问题,change master以后可以复制,但是数据库监控一直报错。
show slave status\G显示的Seconds_Behind_Master:的复制延迟一下子是0,一下子是90多w秒。
以前我们也遇到过类似的问题,具体原因不大记得了(唉,就是没有文档记录的错阿),最后好像是通过重做复制修复了。
尝试重新change master以后还是没有好转。查看错误日志显示如下:
100817 19:45:45 [Note] Slave I/O thread: Failed reading log event, reconnecting to retry, log ‘mysql-bin.000033’ at postion 271148235
100817 19:45:45 [Note] Slave: received end packet from server, apparent master shutdown:
100817 19:45:45 [Note] Slave I/O thread: Failed reading log event, reconnecting to retry, log ‘mysql-bin.000033’ at postion 271148235
100817 19:45:45 [Note] Slave: received end packet from server, apparent master shutdown:
并且不断的在刷,每秒钟有十来条记录。
通过对应的binlog file和binlog position去查看主机对应的记录都没有问题。并且主机到备机的复制只是时断时续,并没有一直停着不往前走。
实在没有办法,最后停掉了复制。这个事情因为并不是那么紧急,所以也暂时放在一边了。

偶尔的机会,我在备机B上也发现了同样的错误,也是Slave: received end packet from server, apparent master shutdown的错误。
并且change master以后,出现的频率变的非常高。
看了一下my.cnf配置文件,注意到B和C配置的server_id都是2,因为C当时是计划替换B的所以server_id也配置为一样了。
是不是这个问题引起的列?试试就知道了,把C的server_id配置成3,数据库B和数据库C检查复制状态,好了!

想起数据库复制的那一块代码。数据库连接master,请求复制开始的时候需要几个参数:
1、四个字节的数据文件开始位置
2、两个字节的binlog flag(目前还没有用上)
3、2个字节的binlog file名字字符串长度
4、4个字节的server_id
5、n个字节的binlog file名字字符串(在前面3里记录了它的长度)
这里向主机申请的时候,需要填写自己的server_id,来通知master连上来的是谁。
如果现在有两个slave连上来并都申明自己的server_id为2,MySQL的master就有点无所适从了。
初步怀疑是slave的IO线程连上以后,master过了一段时间(ms级别的)才发现,然后把新的连接断掉,所以slave io线程才会报警:
100817 19:45:45 [Note] Slave I/O thread: Failed reading log event, reconnecting to retry, log ‘mysql-bin.000033’ at postion 271148235
100817 19:45:45 [Note] Slave: received end packet from server, apparent master shutdown:
说收到主机发起的end packet(主机每次发送一个event过来,如果1、收到的包数据有误或者2、不能由接收到的包解出正确的event来,slave io判定复制有问题,
而其中end packet属于第一种情况
对应的代码为sql/slave.cc的read_event函数:
/* Check if eof packet */
if (len < 8 && mysql->net.read_pos[0] == 254)
{
sql_print_information(“Slave: received end packet from server, apparent ”
“master shutdown: %s”,
mysql_error(mysql));
DBUG_RETURN(packet_error);
}

由于MySQL的io有自动重连的机制,所以MySQL的io线程又连主机,并且还连上了。
master过了一段时间(ms级别的)又发现已经有了server_id=2的slave连着的,把它重新断掉了。
看了一下master的代码sql/sql_parse.cc的dispatch_command函数:
mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags);
unregister_slave(thd,1,1);
发送binlog以后,它会做一个unregister_slave,它认为是同一个slave change master连接过来,“理所当然的”它就把前面的连接给干掉了。
周而复始,就产生了这样的错误。
解决的办法很简单,就是不要用同样的server_id的slave同时连一台master。server_id可以设置的很大,所以不要顾虑,赶紧重设吧。

不知道有没有人想过为什么server_id不能设置为0
在my.cnf里面是没有办法设置server_id=0的,不信的话,你试试,正常情况下,它不会让你start slave的。代码如下sql/slave.cc的start_slave_thread函数:
if (!server_id)
{
if (start_cond)
pthread_cond_broadcast(start_cond);
if (start_lock)
pthread_mutex_unlock(start_lock);
sql_print_error(“Server id not set, will not start slave”);
DBUG_RETURN(ER_BAD_SLAVE);
}
不过,我还真的没有测试过这种情况,呵呵。
当然,server_id设置为0的情况也有,但是不是你或者我,而是mysqlbinlog,它的server_id可以设置为0.
原因如下:如果mysqlbinlog的server_id为0的话,它就会把正常的slave给停掉了。万一它的server_id为5,它连上去以后就会把真正的server-id为5的slave断掉了。
server_id设置为0我印象中还有一个不同点,是关于MySQL master发送binlog是否会停止的,但是就是想不起来了,老了。代码也没有找到确凿的证据;先这样把

may your success!