MySQL 双主单写,主库偶尔出现大量延迟的原因(1)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介: MySQL 双主单写,主库偶尔出现大量延迟的原因

水平有限有误请谅解


一、问题来源

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

image.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线程启动了
            print 0;
//设置延迟为0
         else
            print NULL;
//否则为空值
       }
        else
          compute Seconds_Behind_Master;
//如果SQL线程没有应用完所有的IO线程写入的Event,那么需要计算延迟。
      }
      else
       print NULL;
//如果连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 - 主从服务器时间差 这个公式必须出现差值。

好了接下来带着这两个产生延迟的必要条件来寻求原因。

关于更多延迟计算的细节参考:

https://www.jianshu.com/p/033f83314619

三、产生延迟的原因

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_DESCRIPTION_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(); //忽略到位点
}
  1. 主库: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= new Rotate_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";
goto err;
}
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存在一定的差值 的条件就可能会满足,则进入延迟计算环节。

  1. 主库的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切换的时间 - 主从时间的差值
  1. MTS和单线程的不同

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

        if (!rli->is_parallel_exec())
rli->last_master_timestamp= 0;

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

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

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

  1. 最后需要注意的是实际上这种情况的延迟并没有问题,完全是一种偶尔出现的计算上的问题,是一种假象,如果主库的压力越大出现这种情况的可能性就会越大,因为IO线程和SQL线程在处理Read_Master_Log_Pos和Exec_Master_Log_Pos的出现时间差的可能性就会越大。
            </div>
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
2天前
|
人工智能 运维 安全
|
4天前
|
SpringCloudAlibaba 负载均衡 Dubbo
微服务架构下Feign和Dubbo的性能大比拼,到底鹿死谁手?
本文对比分析了SpringCloudAlibaba框架下Feign与Dubbo的服务调用性能及差异。Feign基于HTTP协议,使用简单,适合轻量级微服务架构;Dubbo采用RPC通信,性能更优,支持丰富的服务治理功能。通过实际测试,Dubbo在调用性能、负载均衡和服务发现方面表现更出色。两者各有适用场景,可根据项目需求灵活选择。
383 124
微服务架构下Feign和Dubbo的性能大比拼,到底鹿死谁手?
|
7天前
|
人工智能 JavaScript 测试技术
Qwen3-Coder入门教程|10分钟搞定安装配置
Qwen3-Coder 挑战赛简介:无论你是编程小白还是办公达人,都能通过本教程快速上手 Qwen-Code CLI,利用 AI 轻松实现代码编写、文档处理等任务。内容涵盖 API 配置、CLI 安装及多种实用案例,助你提升效率,体验智能编码的乐趣。
670 107
|
1天前
|
算法 Python
【轴承故障诊断】一种用于轴承故障诊断的稀疏贝叶斯学习(SBL),两种群稀疏学习算法来提取故障脉冲,第一种仅利用故障脉冲的群稀疏性,第二种则利用故障脉冲的额外周期性行为(Matlab代码实现)
【轴承故障诊断】一种用于轴承故障诊断的稀疏贝叶斯学习(SBL),两种群稀疏学习算法来提取故障脉冲,第一种仅利用故障脉冲的群稀疏性,第二种则利用故障脉冲的额外周期性行为(Matlab代码实现)
221 152
|
3天前
|
Java 数据库 数据安全/隐私保护
Spring 微服务和多租户:处理多个客户端
本文介绍了如何在 Spring Boot 微服务架构中实现多租户。多租户允许单个应用实例为多个客户提供独立服务,尤其适用于 SaaS 应用。文章探讨了多租户的类型、优势与挑战,并详细说明了如何通过 Spring Boot 的灵活配置实现租户隔离、动态租户管理及数据源路由,同时确保数据安全与系统可扩展性。结合微服务的优势,开发者可以构建高效、可维护的多租户系统。
200 127
|
3天前
|
Web App开发 前端开发 API
在折叠屏应用中,如何处理不同屏幕尺寸和设备类型的样式兼容性?
在折叠屏应用中,如何处理不同屏幕尺寸和设备类型的样式兼容性?
230 124
|
1天前
|
编解码 算法 自动驾驶
【雷达通信】用于集成传感和通信的OFDM雷达传感算法(Matlab代码实现)
【雷达通信】用于集成传感和通信的OFDM雷达传感算法(Matlab代码实现)
172 125
|
1天前
|
JavaScript 关系型数据库 MySQL
基于python的网上外卖订餐系统
本系统基于Python与Flask框架,结合MySQL数据库及Vue前端技术,实现了一个功能完善的网上订餐平台。系统涵盖餐品、订单、用户及评价管理模块,并深入研究订餐系统的商业模式、用户行为与服务质量。技术上采用HTML、PyCharm开发工具,支持移动端访问,助力餐饮业数字化转型。