Seconds_Behind_Master 能正确反映主从延时吗?

2023-11-22 01:32

本文主要是介绍Seconds_Behind_Master 能正确反映主从延时吗?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在mysql的备库的监控中有一项很重要的指标:Seconds_Behind_Master,这个值是怎么得到的呢?下面从5.1.58的代码中分析一下:

    mysql的replication中有2个比较重要的class:Master_info(rpl_mi.h), Relay_log_info(rpl_rli.h),他们分别对应于master,info文件和slave.info文件;很显然,Master_info是io_thread需要的,Relay_log_info是sql_thread需要的。Master_info中有一个变量 clock_diff_with_master,这个值记录着mysql的主库和备库的时间差,可以理解为主备的主机时间差。clock_diff_with_master变量的定义如下:

Cpp代码   收藏代码
  1. /* 
  2.    The difference in seconds between the clock of the master and the clock of 
  3.    the slave (second - first). It must be signed as it may be <0 or >0. 
  4.    clock_diff_with_master is computed when the I/O thread starts; for this the 
  5.    I/O thread does a SELECT UNIX_TIMESTAMP() on the master. 
  6.    "how late the slave is compared to the master" is computed like this: 
  7.    clock_of_slave - last_timestamp_executed_by_SQL_thread - clock_diff_with_master 
  8.  
  9. */  
  10. long clock_diff_with_master;  

     这个变量的注释直接提到了Seconds_Behind_Master的计算方法:clock_of_slave - last_timestamp_executed_by_SQL_thread - clock_diff_with_master。clock_of_slave是slave的当前时间--执行show slave status的当前时间。

     先看一下clock_diff_with_master的计算:(slave.cc)。执行”start slave;“/“start slave io_thread;”后,会执行start_slave_threads来启动io thread,io thread启动后首先做的就是获取主库的mysql版本和主库的当前时间(mysql_real_query(mysql, STRING_WITH_LEN("SELECT UNIX_TIMESTAMP()"))),获取到主库的当前时间后,用备库的当前时间减去主库的时间,得到clock_diff_with_master。

Cpp代码   收藏代码
  1. int start_slave_threads(bool need_slave_mutex, bool wait_for_start,  
  2.                         Master_info* mi, const char* master_info_fname,  
  3.                         const char* slave_info_fname, int thread_mask)  
  4. {  
  5. ...  
  6.   if (thread_mask & SLAVE_IO)  
  7.     error=start_slave_thread(handle_slave_io,lock_io,lock_cond_io,  
  8.                              cond_io,  
  9.                              &mi->slave_running, &mi->slave_run_id,  
  10.                              mi, 1); //high priority, to read the most possible  
  11. ...  
  12. }  
  13. ...  
  14. /** 
  15.   Slave IO thread entry point. 
  16.  
  17.   @param arg Pointer to Master_info struct that holds information for 
  18.   the IO thread. 
  19.  
  20.   @return Always 0. 
  21. */  
  22. pthread_handler_t handle_slave_io(void *arg)  
  23. {  
  24. ...  
  25.   thd_proc_info(thd, "Checking master version");  
  26.   ret= get_master_version_and_clock(mysql, mi);  
  27. ...  
  28. }  
  29. ...  
  30. static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)  
  31. {  
  32. ...  
  33.   master_res= NULL;  
  34.   if (!mysql_real_query(mysql, STRING_WITH_LEN("SELECT UNIX_TIMESTAMP()")) &&  
  35.       (master_res= mysql_store_result(mysql)) &&  
  36.       (master_row= mysql_fetch_row(master_res)))  
  37.   {  
  38.     mi->clock_diff_with_master=  
  39.       (long) (time((time_t*) 0) - strtoul(master_row[0], 0, 10));  
  40.   }  
  41.   else if (is_network_error(mysql_errno(mysql)))  
  42.   {  
  43.     mi->report(WARNING_LEVEL, mysql_errno(mysql),  
  44.                "Get master clock failed with error: %s", mysql_error(mysql));  
  45.     goto network_err;  
  46.   }  
  47.   else   
  48.   {  
  49.     mi->clock_diff_with_master= 0; /* The "most sensible" value */  
  50.     sql_print_warning("\"SELECT UNIX_TIMESTAMP()\" failed on master, "  
  51.                       "do not trust column Seconds_Behind_Master of SHOW "  
  52.                       "SLAVE STATUS. Error: %s (%d)",  
  53.                       mysql_error(mysql), mysql_errno(mysql));  
  54.   }  
  55. ...  
  56. }  

     last_timestamp_executed_by_SQL_thread则是sql slave thread最近一次执行的event的时间戳(rpl.rli.cc中的last_event_start_time变量)。而这个变量的更新则是在stmt_done()函数中完成的(rpl.rli.cc),相关定义如下:

Cpp代码   收藏代码
  1. /* 
  2.   Used by row-based replication to detect that it should not stop at 
  3.   this event, but give it a chance to send more events. The time 
  4.   where the last event inside a group started is stored here. If the 
  5.   variable is zero, we are not in a group (but may be in a 
  6.   transaction). 
  7.  */  
  8. time_t last_event_start_time;  
  9.   
  10. /** 
  11.   Helper function to do after statement completion. 
  12.  
  13.   This function is called from an event to complete the group by 
  14.   either stepping the group position, if the "statement" is not 
  15.   inside a transaction; or increase the event position, if the 
  16.   "statement" is inside a transaction. 
  17.  
  18.   @param event_log_pos 
  19.   Master log position of the event. The position is recorded in the 
  20.   relay log info and used to produce information for <code>SHOW 
  21.   SLAVE STATUS</code>. 
  22.  
  23.   @param event_creation_time 
  24.   Timestamp for the creation of the event on the master side. The 
  25.   time stamp is recorded in the relay log info and used to compute 
  26.   the <code>Seconds_behind_master</code> field. 
  27. */  
  28. void stmt_done(my_off_t event_log_pos,  
  29.                time_t event_creation_time);  

     具体的逻辑则是,sql thread启动后,读取relaylog(netxt_event()),apply & update pos(apply_event_and_update_pos()),update_pos的时候判断是否执行到了事务的结束位置,如果执行到了,则调用stmt_done(),stmt_done()会将last_master_timestamp更新为最近一次event的创建时间(event_creation_time)。因此如果在主备基本无延迟的时候,主库执行了一个大事务,你会发现备库延迟突然很大,然后又没了,"延迟跳跃"。

Cpp代码   收藏代码
  1. /** 
  2.   Slave SQL thread entry point. 
  3.  
  4.   @param arg Pointer to Relay_log_info object that holds information 
  5.   for the SQL thread. 
  6.  
  7.   @return Always 0. 
  8. */  
  9. pthread_handler_t handle_slave_sql(void *arg)  
  10. {  
  11. ...  
  12.     if (exec_relay_log_event(thd,rli))  
  13. ...  
  14. }  
  15. ...  
  16. static int exec_relay_log_event(THD* thd, Relay_log_info* rli)  
  17. {  
  18. ...  
  19.   Log_event * ev = next_event(rli);  
  20. ...  
  21.     exec_res= apply_event_and_update_pos(ev, thd, rli);  
  22. ...  
  23. }  
  24. ...  
  25. int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli)  
  26. {  
  27. ...  
  28.     exec_res= ev->apply_event(rli);  
  29. ...  
  30.     int error= ev->update_pos(rli);  
  31. ...  
  32. }  
  33. ....log_event.cc  
  34. int  
  35. Rows_log_event::do_update_pos(Relay_log_info *rli)  
  36. {  
  37. ...  
  38.   if (get_flags(STMT_END_F))  
  39.   {  
  40.     /* 
  41.       Indicate that a statement is finished. 
  42.       Step the group log position if we are not in a transaction, 
  43.       otherwise increase the event log position. 
  44.     */  
  45.     rli->stmt_done(log_pos, when);  
  46.    }  
  47. ...  
  48. }  
  49. ...rpl_rli.cc  
  50. void Relay_log_info::stmt_done(my_off_t event_master_log_pos,  
  51.                                   time_t event_creation_time)  
  52. {  
  53. ...  
  54.   if ((sql_thd->options & OPTION_BEGIN) && opt_using_transactions)  
  55.     inc_event_relay_log_pos();  
  56.   else  
  57.   {  
  58.     inc_group_relay_log_pos(event_master_log_pos);  
  59.     flush_relay_log_info(this);  
  60.     /* 
  61.       Note that Rotate_log_event::do_apply_event() does not call this 
  62.       function, so there is no chance that a fake rotate event resets 
  63.       last_master_timestamp.  Note that we update without mutex 
  64.       (probably ok - except in some very rare cases, only consequence 
  65.       is that value may take some time to display in 
  66.       Seconds_Behind_Master - not critical). 
  67.     */  
  68. #ifndef DBUG_OFF  
  69.     if (!(event_creation_time == 0 && debug_not_change_ts_if_art_event > 0))  
  70. #else  
  71.       if (event_creation_time != 0)  
  72. #endif  
  73.         last_master_timestamp= event_creation_time;  
  74.   }  
  75. }  

    另外一种可能会出现延迟跳跃的情况是:主库的网络有问题,io thread拉binlog的速度很慢,备库实际有很大的延迟,而sql thread执行的时候永远是hot log,这个时候会出现,备库一直有很大的延迟,突然一下延迟没了(=0),接下去延迟又变得很大。这种情况的原因为:sql thread读取relay log是在next_event(slave.cc)中完成的,而next_event中会判断当前的读取的relay log是否是“热的”--正在被io thread使用,如果是"热的",并且读取到当前relay log的最后一个event 或io thread没有再更新relay log的时候,会将last_master_time_stamp置为0,并new一个 Rotate_log_event。而如果在这个时候执行"show slave status",延迟就是0了。这个地方需要补充一点的是“show slave status”和前面提到的逻辑,唯一有争用的锁是&rli->data_lock,但是next_event在恢复last_master_time_stamp前就释放了这个锁:

Cpp代码   收藏代码
  1. ...slave.cc...  
  2. static Log_event* next_event(Relay_log_info* rli)  
  3. {  
  4. ...  
  5.   safe_mutex_assert_owner(&rli->data_lock);  
  6. ...  
  7.     if (!cur_log->error) /* EOF */  
  8.     {  
  9.       /* 
  10.         On a hot log, EOF means that there are no more updates to 
  11.         process and we must block until I/O thread adds some and 
  12.         signals us to continue 
  13.       */  
  14.       if (hot_log)  
  15.       {  
  16.         time_t save_timestamp= rli->last_master_timestamp;  
  17.         rli->last_master_timestamp= 0;  
  18. ...  
  19.         /* 
  20.           We can, and should release data_lock while we are waiting for 
  21.           update. If we do not, show slave status will block 
  22.         */  
  23.         pthread_mutex_unlock(&rli->data_lock);  
  24. ...  
  25.         rli->last_master_timestamp= save_timestamp;  
  26. ...  
  27.        }  
  28. ...  
  29.      }  
  30. ...  
  31. }  
  32. ....  
  33. /** 
  34.   Execute a SHOW SLAVE STATUS statement. 
  35.  
  36.   @param thd Pointer to THD object for the client thread executing the 
  37.   statement. 
  38.  
  39.   @param mi Pointer to Master_info object for the IO thread. 
  40.  
  41.   @retval FALSE success 
  42.   @retval TRUE failure 
  43. */  
  44. bool show_master_info(THD* thd, Master_info* mi)  
  45. {  
  46. ...  
  47.     pthread_mutex_lock(&mi->run_lock);  
  48.     protocol->store(mi->io_thd ? mi->io_thd->proc_info : "", &my_charset_bin);  
  49.     pthread_mutex_unlock(&mi->run_lock);  
  50.   
  51.     pthread_mutex_lock(&mi->data_lock);  
  52.     pthread_mutex_lock(&mi->rli.data_lock);  
  53.     pthread_mutex_lock(&mi->err_lock);  
  54.     pthread_mutex_lock(&mi->rli.err_lock);  
  55. ...  
  56.     /* 
  57.       Seconds_Behind_Master: if SQL thread is running and I/O thread is 
  58.       connected, we can compute it otherwise show NULL (i.e. unknown). 
  59.     */  
  60.     if ((mi->slave_running == MYSQL_SLAVE_RUN_CONNECT) &&  
  61.         mi->rli.slave_running)  
  62.     {  
  63.       long time_diff= ((long)(time(0) - mi->rli.last_master_timestamp)  
  64.                        - mi->clock_diff_with_master);  
  65.       /* 
  66.         Apparently on some systems time_diff can be <0. Here are possible 
  67.         reasons related to MySQL: 
  68.         - the master is itself a slave of another master whose time is ahead. 
  69.         - somebody used an explicit SET TIMESTAMP on the master. 
  70.         Possible reason related to granularity-to-second of time functions 
  71.         (nothing to do with MySQL), which can explain a value of -1: 
  72.         assume the master's and slave's time are perfectly synchronized, and 
  73.         that at slave's connection time, when the master's timestamp is read, 
  74.         it is at the very end of second 1, and (a very short time later) when 
  75.         the slave's timestamp is read it is at the very beginning of second 
  76.         2. Then the recorded value for master is 1 and the recorded value for 
  77.         slave is 2. At SHOW SLAVE STATUS time, assume that the difference 
  78.         between timestamp of slave and rli->last_master_timestamp is 0 
  79.         (i.e. they are in the same second), then we get 0-(2-1)=-1 as a result. 
  80.         This confuses users, so we don't go below 0: hence the max(). 
  81.  
  82.         last_master_timestamp == 0 (an "impossible" timestamp 1970) is a 
  83.         special marker to say "consider we have caught up". 
  84.       */  
  85.       protocol->store((longlong)(mi->rli.last_master_timestamp ?  
  86.                                  max(0, time_diff) : 0));  
  87.     }  
  88.     else  
  89.     {  
  90.       protocol->store_null();  
  91.     }  
  92. ...  
  93. }  

     到这里Seconds_Behind_Master的计算就结束了。总结起来就是Seconds_Behind_Master的计算有3部分组成,备库当前时间,sql thread最近一次执行的event的时间,主备系统的时间差。备库出现延迟跳跃,可能是大事务引起,也可能是网络问题。(ps:这2种情况,都被我遇到过:))

 

链接:

http://www.mysqlperformanceblog.com/2013/09/16/possible-reasons-when-mysql-replication-lag-is-flapping-between-0-and-xxxxx/

这篇关于Seconds_Behind_Master 能正确反映主从延时吗?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/406520

相关文章

【测试】输入正确用户名和密码,点击登录没有响应的可能性原因

目录 一、前端问题 1. 界面交互问题 2. 输入数据校验问题 二、网络问题 1. 网络连接中断 2. 代理设置问题 三、后端问题 1. 服务器故障 2. 数据库问题 3. 权限问题: 四、其他问题 1. 缓存问题 2. 第三方服务问题 3. 配置问题 一、前端问题 1. 界面交互问题 登录按钮的点击事件未正确绑定,导致点击后无法触发登录操作。 页面可能存在

java线程深度解析(四)——并发模型(Master-Worker)

http://blog.csdn.net/daybreak1209/article/details/51372929 二、Master-worker ——分而治之      Master-worker常用的并行模式之一,核心思想是由两个进程协作工作,master负责接收和分配任务,worker负责处理任务,并把处理结果返回给Master进程,由Master进行汇总,返回给客

MySQL主从同步延迟原理及解决方案

概述 MySQL的主从同步是一个很成熟的架构,优点为: ①在从服务器可以执行查询工作(即我们常说的读功能),降低主服务器压力; ②在从主服务器进行备份,避免备份期间影响主服务器服务; ③当主服务器出现问题时,可以切换到从服务器。 相信大家对于这些好处已经非常了解了,在项目的部署中也采用这种方案。但是MySQL的主从同步一直有从库延迟的问题,那么为什么会有这种问题。这种问题如何解决呢? MyS

Redis-主从集群

主从架构 单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。 主从数据同步原理 全量同步 主从第一次建立连接时,会执行全量同步,将master节点的所有数据都拷贝给slave节点,流程: 判断是否是第一次同步,如果是,返回版本信息(replication id 和offset),将salve节点的版本信息变为master的

独立按键单击检测(延时消抖+定时器扫描)

目录 独立按键简介 按键抖动 模块接线 延时消抖 Key.h Key.c 定时器扫描按键代码 Key.h Key.c main.c 思考  MultiButton按键驱动 独立按键简介 ​ 轻触按键相当于一种电子开关,按下时开关接通,松开时开关断开,实现原理是通过轻触按键内部的金属弹片受力弹动来实现接通与断开。  ​ 按键抖动 由于按键内部使用的是机

xss-labs-master通关教程

一.level1 先来进行一下代码审计 <?php ini_set("display_errors", 0);//关闭错误显示$str = $_GET["name"]; //接受URL来的get形式的name传参echo "<h2 align=center>欢迎用户".$str."</h2>";//在网页输出,并不是echo执行的,而是echo把HTML代码发送到浏览器,浏览器对发送的H

使用shell脚本安装mysql8,进行主从备份配置

思路 在3台主机上安装mysql进行主从备份配置 使用rpm包yum安装mysql 首先,我们要准备好安装文件,首先下载rpm包 wget -P "/opt/" https://repo.mysql.com//mysql80-community-release-el7-3.noarch.rpm 然后执行安装(默认已配置阿里云的yum仓库 yum -y install mysql80

Dungeon Master -uva

一个简答的三维BFS遍历,我从中领悟到了惨痛的教训,关于栈的溢出!!! 不多说了。。郁闷 #include<stdio.h>#include<string.h>#include<stdlib.h>#define MAX_SIZE 50 + 10int Dung[MAX_SIZE][MAX_SIZE][MAX_SIZE];int time[MAX_SIZE][MAX_SIZE][M

Circuit Design 三极管驱动蜂鸣器电路 及 蜂鸣器两端电压正确但是不响的解决方案

利用三极管进行电流放大的蜂鸣器驱动电路图: (百度图片找的) 我用有源蜂鸣器实现的这个电路,但是蜂鸣器不响。 details: 1. VCC =5V 蜂鸣器两端的直接电压约为4.5V, 但是蜂鸣器不响。 2. 将蜂鸣器直接接在4.5V的电源两端,蜂鸣器响。(说明蜂鸣器是好的) 3. 测了三极管各个管脚的电压, 和理论上的是一致的。 情况很奇怪,换了好几个三极管结果都是一样的,

ubuntu 20.04 一直卡在登录界面,即使密码正确也无法登录(失败记录)

ubuntu 20.04 一直卡在登录界面,即使密码正确也无法登录 这次是装实体机,一次失败的尝试。。。 名称型号CPUIntel Xeon E5-2673 V3GPURTX 3060 mobile 安装的时候不要选install third-party software for graphics and Wi-fi hardware and additional media