mysql主主会出现什么情况_MySQL 双主单写,主库偶尔出现大量延迟的原因

原标题:MySQL 双主单写,主库偶尔出现大量延迟的原因

版本:5.7.29

水平有限有误请谅解

一、问题来源

这是来自我们线上数据库的一个问题。我们是双主单写,这里约定写入的库为主库,没有写入的库为从库。我们的falcon偶尔会进行报警如下(频率很低):

075def71bc0569a09ac32f1ef33da6bf.png

这是非常奇怪的,按理说我是单写的从库没有做任何操作(除了应用Event以外),主库哪来的延迟,并且延迟这么大。 在我映像中有朋友问过这个问题,当时没有细细研究。

二、延迟计算的规则

我们还是要看看主从计算延迟的伪代码:

/*

The pseudo code to compute Seconds_Behind_Master:

if(SQL thread is running)

//如果SQL线程启动了

{

if(SQL thread processed all the available relay log)

//如果SQL线程已经应用完了所有的IO线程写入的Event

{

if(IO thread is running)

//如果IO线程启动了

print0;

//设置延迟为 0

else

printNULL;

//否则为空值

}

else

compute Seconds_Behind_Master;

//如果SQL线程没有应用完所有的IO线程写入的Event,那么需要计算延迟。

}

else

printNULL;

//如果连SQL线程也没有启动则设置为空值

*/

计算延迟的公式为:

long time_diff= ((long)(time( 0)

- mi->rli->last_master_timestamp)

- mi->clock_diff_with_master);

也就是:

服务器当前时间-Event header中的timestamp - 主从服务器时间差

出现延迟的必要条件:

如果SQL线程没有应用完了所有的IO线程写入的Event,也就是Read_Master_Log_Pos和Exec_Master_Log_Pos存在一定的差值。判定标准为(mi->get_master_log_pos == mi->rli->get_group_master_log_pos) &&

(!strcmp(mi->get_master_log_name, mi->rli->get_group_master_log_name))

抛开文件名,也就是通过 IO线程读取到主库binary log的位置 和 SQL线程应用到的主库binary log位置进行比较来进行 判断,只要他们出现差值就会进入延迟计算环节。

服务器当前时间-Event header中的timestamp - 主从服务器时间差 这个公式必须出现差值。

三、产生延迟的原因

1.主库:首先主库写到从库的Event,从库会写入到binlog(log_slave_updates 开启),并且从库的DUMP线程会发送给主库,但是主库的IO线程通过SERVER_ID进程判定,将Event进行过滤,不写入主库的relay log,同时会更新主库IO线程读取的位置(Read_Master_Log_Pos),并且更新忽略到的位置(rli->ign_master_log_name_end[0])。代码如下:

if(!(s_id == ::server_id && !mi->rli->replicate_same_server_id) ||

(event_type != binary_log::FORMAT_DEION_EVENT &&

event_type != binary_log::ROTATE_EVENT &&

event_type != binary_log::STOP_EVENT))

{

mi->set_master_log_pos(mi->get_master_log_pos + inc_pos); //增加Read_Master_Log_Pos位点,为当前位置

memcpy(rli->ign_master_log_name_end, mi->get_master_log_name, FN_REFLEN); //进行拷贝

DBUG_ASSERT(rli->ign_master_log_name_end[ 0]); //断言存在

rli->ign_master_log_pos_end= mi->get_master_log_pos; //忽略到位点

}

主库:SQL线程会通过rli->ign_master_log_name_end[0]判定是否有需要跳过的Event,如果有则构建一个Rotate_log_event来跳过这个Event,代码如下:

if(rli->ign_master_log_name_end[

0])

//如果跳过的Event存在

{

/* We generate and return a Rotate, to make our positions advance */

DBUG_PRINT( "info",( "seeing an ignored end segment"));

ev= newRotate_log_event(rli->ign_master_log_name_end,

0, rli->ign_master_log_pos_end, exec_relay_log_event

Rotate_log_event::DUP_NAME); //构建一个Rotate Event,位置为

rli->ign_master_log_name_end[ 0]= 0; //rli->ign_master_log_pos_end,执行这个Event就可以

mysql_mutex_unlock(log_lock);exec_relay_log_event //来更新Exec_Master_Log_Pos位点

if(unlikely(!ev))

{

errmsg= "Slave SQL thread failed to create a Rotate event "

"(out of memory?), SHOW SLAVE STATUS may be inaccurate";

gotoerr;

}

ev->server_id= 0; // don't be ignored by slave SQL thread

DBUG_RETURN(ev);

}

好了到这里我们知道了Event在主库是如何跳过的,但是注意IO线程和SQL线程在处理Read_Master_Log_Pos和Exec_Master_Log_Pos的时候可能有一定的时间差,那么Read_Master_Log_Pos和Exec_Master_Log_Pos存在一定的差值 的条件就可能会满足,则进入延迟计算环节。

主库的SQL线程平时并没有读取到Event,因为所有的Event都被IO线程过滤掉了。因此 Event的 header中的timestamp 不会更新(MTS)。但是如果从库binlog切换的时候,从库至少会传送ROTATE_EVENT给主库,这个时候主库会拿到这个实际的Event,因此Event的 header中的timestamp 更新了。如果刚好遇到主库的IO线程的Read_Master_Log_Pos和Exec_Master_Log_Pos有差值, 那么falcon去查看延迟就会得到一个延迟很大的假象,延迟的计算公式就会变为如下:

主库当前的时候 - 从库上次binlog切换的时间 - 主从时间的差值

MTS和单线程的不同

上面的第3点只适用于MTS,单SQL线程不同,会去将last_master_timestamp设置为0,代码如下:

if(!rli->is_parallel_exec)

rli->last_master_timestamp= 0;

言外之意单SQL线程计算延迟的公式为:

主库当前的时间 - 1970年1月1日0点 - 主从时间的差值

因此看起来计算出来的延迟会更大。

最后需要注意的是实际上这种情况的延迟并没有问题,完全是一种偶尔出现的计算上的问题,是一种假象,如果主库的压力越大出现这种情况的可能性就会越大,因为IO线程和SQL线程在处理Read_Master_Log_Pos和Exec_Master_Log_Pos的出现时间差的可能性就会越大。

四、MTS下的延迟debug

其实知道了原理就很容易debug了,因为我们可以将断点放到主库的show_slave_status_send_data函数上,那么就能看出来了,做的操作如下:

从库flush binary logs

主库执行一些insert操作

主库show slave status

这个时候我们可以跳过(Read_Master_Log_Pos和Exec_Master_Log_Pos存在一定的差值)这个条件,直接通过公式去计算,得到如下结果:

(gdb) p (long)(time( 0)- mi->rli->last_master_timestamp)- mi->clock_diff_with_master

$ 6= 37

延迟就是37秒,因此我们的理论得到了验证。

下面一个debug结果是单SQL线程的,可以看到延迟更是大得离谱。

(gdb) p (long)(time( 0)- mi->rli->last_master_timestamp)- mi->clock_diff_with_master

$ 7= 1592672402

五、其他问题

额外的问题:

如果双主双写

S1

S2

T1

T2

T3

如果按照上面的理论那么T3的更新的位置可能会被T2事务的位点重置。因为主库的SQL线程通过构建的Rotate_log_event可能会出现Exec_Master_Log_Pos倒退的可能性,这显然是不行的。但是代码中构建Rotate_log_event的逻辑包裹在如下逻辑下面。

if(!cur_log->error) /* EOF *///当前relay log 已经读取完了

{

/*

On a hot log, EOF means that there are no more updates to

process and we must block until I/O thread adds some and

signals us to continue

*/

if(hot_log) //如果是 当前relay log

我们可以看到只有在当前 relay log读取完成后才会进行Rotate_log_event的构建。因此不存在此问题。

问题如上虽然不构建Rotate_log_event,但是如果rli->ign_master_log_name_end[0]如果一直保留那么当relay log应用完成后,依旧会去构建Rotate_log_event导致Exec_Master_Log_Pos倒退,实际上这个问题也不会出现,因为在每次IO线程Event写入到relay log后会重置,如下:rli->ign_master_log_name_end[

0]=

0;

//last event

is

notignored

Enjoy MySQL :)返回搜狐,查看更多

责任编辑:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值