【MySQL源码】Seconds_Behind_Master是如何计算的

作为MySQL DBA,相信大家对参数  `Seconds_Behind_Master` 并不陌生,该字段的值可以通过 show  slave status\G的输出,表示主从延迟的时间,单位为秒。监控主从延迟一般取这个值就足够了。0 表示无延迟,理想状态该值不要超过1。如果业务场景中有写后读,而且读的是从库,那么就会对主从延迟要求非常高。

理解了该字段的含义,大家有没有探究过该值是如何计算的?相信大家都可以说出来 是从库的时间 减去 SQL_thread应用到的主库binlog event的时间戳。

今天从官方文档 和 MySQL源码 探寻正确的计算方法 

MySQL官方文档如何解释

原文

翻译

Seconds_Behind_Master

该字段表示复制实例相对于主实例的“延迟”:

  • 当复制实例正在处理更新时,该字段显复制实例上当前时间戳正在处理的事件在主实例上原始时间戳之间的差异。

  • 当复制实例上没有正在处理的事件时,该值为0。

基本上,该字段测量复制实例的SQL线程复制实例的I/O线程之间的秒数差异。如果主实例和复制实例之间的网络连接很快,复制实例的I/O线程非常接近主实例,因此该字段是复制实例的SQL线程相对于主实例的延迟的良好近似值。如果网络速度较慢,这就不是一个良好的近似值;复制实例的SQL线程可能经常追赶读取速度较慢的复制实例的I/O线程,因此Seconds_Behind_Master经常显示为0,即使I/O线程相对于主实例是延迟的。换句话说,该列仅在快速网络中才有用。

即使主实例和复制实例的时钟时间不同,只要在复制实例的I/O线程启动时计算的差异保持不变,此时间差异计算就有效。任何更改,包括NTP更新,都可能导致时钟偏差,使Seconds_Behind_Master的计算变得不太可靠。

在MySQL 5.7中,如果复制实例的SQL线程未运行,或者SQL线程已经消耗完中继日志并且复制实例的I/O线程未运行,该字段为NULL(未定义或未知)。 (在MySQL的早期版本中,如果复制实例的SQL线程或复制实例的I/O线程未运行或未连接到主实例,则该字段为NULL。)如果I/O线程正在运行但中继日志用尽,Seconds_Behind_Master设置为0。

Seconds_Behind_Master的值基于存储在事件中的时间戳,这些事件通过复制进行保留。这意味着如果主实例M1本身是主实例M0的复制实例,M1二进制日志中源自M0二进制日志的任何事件都具有M0该事件的时间戳。这使得MySQL能够成功地复制时间戳。然而,对于Seconds_Behind_Master而言,问题在于如果M1还直接从客户端接收更新,那么Seconds_Behind_Master的值会随机波动,因为M1的最后一个事件有时源自M0,有时是M1上的直接更新的结果。

在使用多线程复制时,您应该记住该值基于Exec_Master_Log_Pos,因此可能不反映最近提交的事务的位置。

官方文档的翻译也有两种计算方法 :

方法一:该字段显复制实例上当前时间戳正在处理的事件在主实例上原始时间戳之间的差异

方法二:该字段测量复制实例的SQL线程复制实例的I/O线程之间的秒数差异,前提主实例和复制实例之间的网络连接很快,复制实例的I/O线程非常接近主实例

源码中如何计算

在源码中全局搜索 Seconds_Behind_Master 关键字 ,排除测试文件,出现Seconds_Behind_Master关键字的文件也就几个。找到关于计算Seconds_Behind_Master 源码。

注释中也给出了 计算Seconds_Behind_Master 的伪代码 

  /*
     The pseudo code to compute Seconds_Behind_Master:
     if (SQL thread is running)
     {
       if (SQL thread processed all the available relay log)
       {
         if (IO thread is running)
            print 0;
         else
            print NULL;
       }
        else
          compute Seconds_Behind_Master;
      }
      else
       print NULL;
  */

// 伪代码:计算 Seconds_Behind_Master

if (SQL 线程正在运行) {
    if (SQL 线程已处理所有可用的中继日志) {
        if (IO 线程正在运行) {
            输出 0;  // I/O 线程正在运行,但中继日志已用尽,因此 Seconds_Behind_Master 设置为 0。
        } else {
            输出 NULL;  // I/O 线程不在运行,因此 Seconds_Behind_Master 为 NULL。
        }
    } else {
        计算 Seconds_Behind_Master;  // 计算复制实例相对于主实例的延迟。
    }
} else {
    输出 NULL;  // SQL 线程不在运行,因此 Seconds_Behind_Master 为 NULL。
}

真实代码 

  if (mi->rli->slave_running)
  {
    /*
       Check if SQL thread is at the end of relay log
       Checking should be done using two conditions
       condition1: compare the log positions and
       condition2: compare the file names (to handle rotation case)
    */
    if ((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())))
    {
      if (mi->slave_running == MYSQL_SLAVE_RUN_CONNECT)
        protocol->store(0LL);
      else
        protocol->store_null();
    }
    else
    {
      long time_diff= ((long)(time(0) - mi->rli->last_master_timestamp)
                       - mi->clock_diff_with_master);
      /*
        Apparently on some systems time_diff can be <0. Here are possible
        reasons related to MySQL:
        - the master is itself a slave of another master whose time is ahead.
        - somebody used an explicit SET TIMESTAMP on the master.
        Possible reason related to granularity-to-second of time functions
        (nothing to do with MySQL), which can explain a value of -1:
        assume the master's and slave's time are perfectly synchronized, and
        that at slave's connection time, when the master's timestamp is read,
        it is at the very end of second 1, and (a very short time later) when
        the slave's timestamp is read it is at the very beginning of second
        2. Then the recorded value for master is 1 and the recorded value for
        slave is 2. At SHOW SLAVE STATUS time, assume that the difference
        between timestamp of slave and rli->last_master_timestamp is 0
        (i.e. they are in the same second), then we get 0-(2-1)=-1 as a result.
        This confuses users, so we don't go below 0: hence the max().

        last_master_timestamp == 0 (an "impossible" timestamp 1970) is a
        special marker to say "consider we have caught up".
      */
      protocol->store((longlong)(mi->rli->last_master_timestamp ?
                                   max(0L, time_diff) : 0));
    }
  }
  else
  {
    protocol->store_null();
  }

具体计算方式 

      long time_diff= ((long)(time(0) - mi->rli->last_master_timestamp)
                       - mi->clock_diff_with_master);

这行代码计算了一个名为 time_diff 的长整型变量,其值是当前时间(通过 time(0) 获取)与主实例上最后一个事件的时间戳mi->rli->last_master_timestamp)之间的差异减去主从实例之间的时钟差异mi->clock_diff_with_master)。这个 time_diff 的值表示当前时间与主实例最后一个事件的时间戳之间的秒数差异,减去主从实例之间的时钟差异。

   clock_of_slave - last_timestamp_executed_by_SQL_thread - clock_diff_with_master
 

  /*
     The difference in seconds between the clock of the master and the clock of
     the slave (second - first). It must be signed as it may be <0 or >0.
     clock_diff_with_master is computed when the I/O thread starts; for this the
     I/O thread does a SELECT UNIX_TIMESTAMP() on the master.
     "how late the slave is compared to the master" is computed like this:
     clock_of_slave - last_timestamp_executed_by_SQL_thread - clock_diff_with_master

  */


/*
   主时钟和从属时钟之间的差异(second - first)。它必须是有符号的,因为它可能 <0 或 >0。
   clock_diff_with_master 是在 I/O 线程启动时计算的;
   为此,I/O 线程在主实例上执行 SELECT UNIX_TIMESTAMP()。
   “从属相对于主实例的延迟”是这样计算的:
   clock_of_slave - last_timestamp_executed_by_SQL_thread - clock_diff_with_master
*/

参考 

官方文档 Seconds_Behind_Master 解释

MySQL :: MySQL 5.7 Reference Manual :: 13.7.5.34 SHOW SLAVE STATUS Statement

MySQL 复制延迟 Seconds_Behind_Master 究竟是如何计算的 - 知乎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/344699.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

selenium执行出现异常,SessionNotCreatedException ChromeDriver only supports

问题现状&#xff1a; 运行程序报错&#xff1a; selenium.common.exceptions.SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version 114 Current browser version is 121.0.6167.85 with binary path /App…

GraphicsMagick 的 OpenCL 开发记录(二十一)

文章目录 支持windows平台windows平台不能生成内核的.bin文件_aligned_free()和free()不匹配的问题 <2022-04-13 Wed> 支持windows平台 支持windows平台需要做的&#xff1a; 为GraphicsMagick/VisualMagick/configure/configure.exe增加“Enable OpenCL”多选框。 从…

【jQuery入门】链式编程、修改css、类操作和className的区别

文章目录 前言一、链式编程二、修改css2.1 获取css的值2.2 设置单个css属性2.3 设置类样式添加类移除类切换类 三、类操作与className的区别总结 前言 jQuery是一个流行的JavaScript库&#xff0c;广泛用于简化DOM操作和处理事件。在jQuery中&#xff0c;链式编程是一种强大的…

Tree-Shaking 作用和实现原理

一、什么是Tree-shaking Tree-shaking 它的名字来源于通过摇晃&#xff08;shake&#xff09;JavaScript代码的抽象语法树&#xff08;AST&#xff09;&#xff0c;是一种用于优化JavaScript代码的技术&#xff0c;主要用于移除未被使用的代码&#xff0c;使得最终生成的代码包…

【机组】计算机组成原理实验指导书.

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《机组 | 模块单元实验》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 ​ 目录 第一章 性能特点 1.1 系…

OpenTCS IDEA 全流程搭建和运行指南

OpenTCS IDEA 全流程搭建和运行指南 JDK安装下载JDK版本 openTCS源码下载IDEA 打开运行查看下载源码中gradle版本号下载gradle 二进制文件配置IDEA Gradle本地仓库IDEA打开openTCS项目运行顺序 JDK安装 下载JDK版本 JDK网址 注意&#xff1a; 请下载官方文档标准的java13JDK o…

httpClient忽略https的证书认证

忽略https证书认证代码: /*** 创建模拟客户端&#xff08;针对 https 客户端禁用 SSL 验证&#xff09;* return* throws Exception*/public static CloseableHttpClient createHttpClientWithNoSsl() throws Exception {// Create a trust manager that does not validate cer…

软考复习之UML设计篇

UML统一建模语言 构件图&#xff1a;描述系统的物理结构&#xff0c;它可以用来显示程序代码如何分解成模块 部署图&#xff1a;描述系统中硬件和软件的物理结构&#xff0c;它描述构成系统架构的软件构件&#xff0c;处理器和设备 用例图&#xff1a;描述系统与外部系统及用…

C++读取txt文件中的逐个字符

为了增加读取的灵活性&#xff0c;所以separator和filename都设置为在主函数中获取输入或者在函数中传参的视线方法 举个例子&#xff0c;txt文件如下&#xff1a; household;2;true; 首先声明一个读取数据的文件 void read_data_file(const string& filename,char se…

【STM32】USB程序烧录需要重新上电 软件复位方法

文章目录 一、问题二、解决思路2.1 直接插拔USB2.2 给芯片复位 三、解决方法3.1 别人的解决方法3.2 在下载界面进行设置 一、问题 最近学习STM32的USB功能&#xff0c;主要是想要使用虚拟串口功能&#xff08;VCP&#xff09;&#xff0c;发现每次烧录之后都需要重新上电才可以…

训练模型时 遇到速度过慢时的深思 速度提升 (From GPU CPU)

训练模型时 遇到速度过慢时的深思 & 速度提升 GPU查看GPU使用情况 配置单机多卡并行训练torch.nn.DataParallel平衡DataParallel带来的显存使用不平衡的问题torch.nn.parallel.DistributedDataParallel 多机多gpu训练Reference 使用半精度训练更好的显卡&#xff0c;更轻的…

编译和链接---C语言

引言 众所周知&#xff0c;C语言是一门高级的编程语言&#xff0c;是无法被计算机直接读懂的&#xff0c;C语言也不同于汇编PHP&#xff0c;无法直接翻译成机器语言&#xff0c;在学习的过程中&#xff0c;你是否好奇过我们所敲的C语言代码&#xff0c;是如何一步步翻译成机器…

Docker容器引擎(1)

目录 一.Docker 概述 为什么要用到容器&#xff1f; docker是什么&#xff1f; 容器与虚拟机的区别&#xff1f; docker的三个核心概念&#xff1a; 二.安装docker 安装依赖包&#xff1a; 安装 Docker-CE并设置为开机自动启动&#xff1a; 查看 docker 版本信息&#…

10个常考的前端手写题,你全都会吗?(下)

前言 &#x1f4eb; 大家好&#xff0c;我是南木元元&#xff0c;热爱技术和分享&#xff0c;欢迎大家交流&#xff0c;一起学习进步&#xff01; &#x1f345; 个人主页&#xff1a;南木元元 今天接着上篇再来分享一下10个常见的JavaScript手写功能。 目录 1.实现继承 ES5继…

docker 部署springboot项目

新建Dockerfile ## AdoptOpenJDK 停止发布 OpenJDK 二进制&#xff0c;而 Eclipse Temurin 是它的延伸&#xff0c;提供更好的稳定性 ## 感谢复旦核博士的建议&#xff01;灰子哥&#xff0c;牛皮&#xff01; FROM eclipse-temurin:8-jre## 将后端项目的 Jar 文件&#xff0c…

软考复习之多媒体篇

常用的计算公式 数据传输率&#xff08;单位:b/s&#xff09; 未压缩的数据传输率 采样频率&#xff08;Hz&#xff09;* 量化位数&#xff08;位&#xff09;* 声道数 波形声音经过数字化后的信息数据量&#xff08;单位:字节&#xff09; 声音信号数据量 数据传输率 * …

简单实现网络编程

1. 前置知识 在学习网络编程前&#xff0c;我们需要先了解一些前置知识 1.1 客户端和服务器 在网络编程中&#xff0c;客户端和服务器是两个关键的角色。 客户端是发起连接并向服务器发送请求的一方。客户端通常是一个应用程序或设备&#xff0c;通过与服务器建立连接&…

白盒测试和黑盒测试的区别

黑盒测试 等价类划分 白盒测试 灰盒测试

K8S图像化工具rancher

Rancher是一个开源的企业级多集群的k8s管理平台 Rancher和k8s的区别 都是为了容器的调度和编排系统&#xff0c;但是rancher不仅能够调度&#xff0c;还能挂历k8s集群&#xff0c;自带监控&#xff08;普罗米修斯&#xff09;&#xff0c;你哪怕不知带k8s是什么&#xff0c;一样…

跟着pink老师前端入门教程-day09

二十二、定位 22.1 为什么需要定位 1. 某个元素可以自由的在一个盒子内移动位置&#xff0c;并且压住其他盒子 2. 当我们滚动窗口时&#xff0c;盒子是固定屏幕某个位置的 解决方法&#xff1a; 1. 浮动可以让多个块级盒子一行没有缝隙排列显示&#xff0c;经常用于横向排…