技术分享 | mysql show processlist Time 为负数的思考「建议收藏」

技术分享 | mysql show processlist Time 为负数的思考「建议收藏」作者:高鹏 文章末尾有他著作的《深入理解 MySQL 主从原理 32 讲》,深入透彻理解 MySQL 主从,GTID 相关技术知识。 一、问题来源 这是一个朋友问我的一个问题,问题如下,在 MTS …

作者:高鹏 文章末尾有他著作的《深入理解 MySQL 主从原理 32 讲》,深入透彻理解 MySQL 主从,GTID 相关技术知识。

一、问题来源

这是一个朋友问我的一个问题,问题如下,在 MTS 中 Worker 线程看到 Time 为负数是怎么回事,如下:

技术分享 | mysql show processlist Time 为负数的思考「建议收藏」

二、关于 show processlist 中的 Time

实际上 show processlist 中的信息基本都来自函数 mysqld_list_processes,也就是说每次执行 show processlist 都需要执行这个函数来进行填充。对于 Time 值来讲它来自如下信息:

Percona:
time_t now= my_time(0);
protocol->store_long ((thd_info->start_time > now) ? 0 :  (longlong) (now - thd_info->start_time));
官方版:
time_t now= my_time(0);
protocol->store_long ((longlong) (now - thd_info->start_time));

代码100分

我们可以注意到在 Percona 的版本中对这个输出值做了优化,也就是如果出现负数的时候直接显示为 0,但是官方版中没有这样做,可能出现负数。

三、计算方式解读和测试

现在我们来看看这个简单的计算公式,实际上 now 很好理解就是服务器的当前时间,重点就在于 thd_info->start_time的取值来自何处。实际这个时间来自于函数 THD::set_time,但是需要注意的是这个函数会进行重载,有下面三种方式:

重载 1

代码100分inlinevoid set_time()
{
    start_utime= utime_after_lock= my_micro_time();
if(user_time.tv_sec || user_time.tv_usec)
{
      start_time= user_time;
}
else
      my_micro_time_to_timeval(start_utime, &start_time);
...
}

重载 2

 inlinevoid set_time(conststruct timeval *t)
 {
    start_time= user_time= *t;
    start_utime= utime_after_lock= my_micro_time();
...
}

重载 3

代码100分void set_time(QUERY_START_TIME_INFO *time_info)
{
    start_time= time_info->start_time;
    start_utime= time_info->start_utime;
}

其实简单的说就是其中有一个 start_utime,如果设置了 start_utime 那么 start_time 将会指定为 start_utime,并且在重载 1 中将会不会修改 start_time,这一点比较重要。好了说了 3 种方式,我们来看看 Time 的计算有如下可能。

1、执行命令 如果主库执行常见的命令都会在命令发起的时候调用重载 1,设置 start_time 为命令开始执行的时间如下:

堆栈:
#0  THD::set_time (this=0x7ffe7c000c90) at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_class.h:3505
#1  0x00000000015c5fe8 in dispatch_command (thd=0x7ffe7c000c90, com_data=0x7fffec03fd70, command=COM_QUERY)
    at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_parse.cc:1247

可以看到这个函数没有实参,因此 start_time 会设置为当前时间,那 Time 的计算公式 now – thdinfo->start_time 就等于 (服务器当前时间 – 命令开始执行的时间)。

2、从库单 Sql 线程和 Worker 线程

其实不管单 Sql 线程还是 Worker 线程都是执行 Event 的,这里的 start_time 将会被设置为 Event header 中 timestamp 的时间(query event/dml event),这个时间实际就是主库命令发起的时间。如下:

堆栈:
query event:

#0  THD::set_time (this=0x7ffe78000950, t=0x7ffe701ec430) at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/sql_class.h:3526
#1  0x00000000018459ab in Query_log_event::do_apply_event (this=0x7ffe701ec310, rli=0x7ffe7003c050, query_arg=0x7ffe701d88a9 "BEGIN", q_len_arg=5)
    at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/log_event.cc:4714

堆栈:
dml event:
#0  THD::set_time (this=0x7ffe78000950, t=0x7ffe701ed5b8) at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/sql_class.h:3526
#1  0x000000000185aa6e in Rows_log_event::do_apply_event (this=0x7ffe701ed330, rli=0x7ffe7003c050)
    at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/log_event.cc:11417

我们看到这里有一个实参的传入我们看一下代码如下:

thd->set_time(&(common_header->when))

实际上就是这一行,这是我们前面说的重载 3,这样设置后 start_utime 和 start_time 都将会设置,即便调用重载 1 也不会更改, 那 Time 的计算方式 now-thd_info->start_time 就等于(从库服务器当前时间 – Event header 中的时间),但是要知道 Event header 中的时间实际也是来自于主库命令发起的时间。既然如此如果从库服务器的时间小于主库服务器的时间,那么 Time 的结果可能是负数是可能出现的,当然 Percona 版本做了优化负数将会显示为 0,如果从库服务器的时间大于主库的时间那么可能看到 Time 比较大。因为我的测试环境都是 Percona,为了效果明显,我们来测试一下 Worker 线程 Time 很大的情况,如下设置:

主库:
[root@mysqltest2 test]# date
FriNov101:40:54 CST 2019

从库:
[root@gp1 log]# date
TueNov1915:58:37 CST 2019

主库随便做一个命令,然后观察如下:

技术分享 | mysql show processlist Time 为负数的思考「建议收藏」

3、设置 timestamp

如果手动指定 timestamp 也会影响到 Time 的计算结果,因为 start_utime 和 start_time 都将会设置,如下:

mysql> set timestamp=1572540000

堆栈:
#0  THD::set_time (this=0x7ffe7c000c90, t=0x7fffec03db30) at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_class.h:3526
#1  0x000000000169e509 in update_timestamp (thd=0x7ffe7c000c90, var=0x7ffe7c006860) at /mysqldata/percona-server-locks-detail-5.7.22/sql/sys_vars.cc:4966
#2  0x00000000016b9a3d in Sys_var_session_special_double::session_update (this=0x2e68e20, thd=0x7ffe7c000c90, var=0x7ffe7c006860)
    at /mysqldata/percona-server-locks-detail-5.7.22/sql/sys_vars.h:1889

我们看到带入了实参,我们看看代码这一行如下:

 thd->set_time(&tmp);

这就是重载 2 了,这样设置后 start_utime 和 start_time 都将会设置,即便调用重载 1 也不会更改,言外之意就是设置了 timestamp 后即便执行了其他的命令 Time 也不会更新。Time 的计算方式 now – thdinfo->starttime 就等于 (服务器当前时间 – 设置的 timestamp 时间),这样的话就可能出现 Time 出现异常,比如很大或者为负数(Percona 为 0)如下:

技术分享 | mysql show processlist Time 为负数的思考「建议收藏」

4、空闲情况下

如果我们的会话空闲状态下那么 now-thd_info->start_time公式中,now 会不断变大,但是 thd_info->start_time 却不会改变,因此 Time 会不断增大,等待到下一次命令到来后才会更改。

四、延伸

这里我想在说明一下如果从库开启了 log_slave_updates 的情况下,从库记录会记录来自主库的 Event,但是这些 Event 的 timestamp 和 Query Event 的 exetime 如何取值呢?

Event 的 timestamp 的取值

其实上面我已经说了,因为 start_time 将会被设置为 Event header 中 timestamp 的时间(query event/dml event),当记录 Evnet 的时候这个时间和主库基本一致,如下:

技术分享 | mysql show processlist Time 为负数的思考「建议收藏」

很明显我们会发现这些 Event 的 timestamp 不是本地的时间,而是主库的时间。

Query Event 的 exetime

我们先来看看这个时间的计算方式:

ulonglong micro_end_time= my_micro_time();//这里获取时间 query event
  my_micro_time_to_timeval(micro_end_time, &end_time);

  exec_time= end_time.tv_sec - thd_arg->start_time.tv_sec;//这里计算时间

相信对于 thd_arg->start_time 而言已经不再陌生,它就是主库命令发起的时间。我在我的《深入理解主从原理》系列中说过了,对于 Query Event 的 exetime 在 row 格式 binlog 下,DML 语句将会是第一行语句修改时间的时间,那么我们做如下定义(row 格式 DML 语句):

  • 主:主库第一行数据修改完成的服务器时间 – 主库本命令发起的时间
  • 从:从库第一行数据修改完成的服务器时间 – 主库本命令发起的时间

他们的差值就是:(从库第一行数据修改完成的服务器时间 – 主库第一行数据修改完成的服务器时间 )同样如果我们从库的时间远远大于主库的时间,那么 exetime 也会出现异常如下:

技术分享 | mysql show processlist Time 为负数的思考「建议收藏」

最后:

Time 是我们平时关注的一个指标,我们经常用它来表示我的语句执行了多久,但是如果出现异常的情况我们也应该能够明白为什么,这里我将它的计算方式做了一个不完全的解释,希望对大家有帮助。当然对于主从部分或者 Event 部分可以参考我的《深入理解主从原理》系列。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/10623.html

(0)
上一篇 2022-12-24
下一篇 2022-12-24

相关推荐

  • Python 3Try:从零开始学习Python

    Python 3Try:从零开始学习Python随着人工智能技术的不断发展,Python已经成为最流行的编程语言之一。Python是一种易学易用的语言,因此也就很适合初学者来学习。如果你是新手,那么Python 3Try绝对是你入门学习的不二选择。Python 3Try可以帮助你快速掌握Python基础知识,并教你如何编写实际而有用的Python程序。

    2024-08-08
    28
  • 实时计算框架:Flink集群搭建与运行机制

    实时计算框架:Flink集群搭建与运行机制Flink是一个框架和分布式处理引擎,用于对无界和有界数据流进行有状态计算。Flink被设计在所有常见的集群环境中运行,以内存执行速度和任意规模来执行计算。主要特性包括:批流一体化、精密的状态管理、事

    2023-04-15
    160
  • MSSQL·备份数据库中的单表[通俗易懂]

    MSSQL·备份数据库中的单表[通俗易懂]阅文时长 | 0.11分钟 字数统计 | 237.6字符 主要内容 | 1、引言&背景 2、声明与参考资料 『MSSQL·备份数据库中的单表』 编写人 | SCscHero 编写时间

    2023-04-15
    159
  • 如何查看 Python 路径

    如何查看 Python 路径Python是一种高级编程语言,常用于网站、科学计算和人工智能等领域。在进行Python编程过程中,我们有时需要知道Python的安装路径,以方便在命令行或IDE中进行Python环境变量的设置。本文将介绍几种方法来查看Python路径。

    2024-04-27
    71
  • mongodb 更新嵌套数组的值[通俗易懂]

    mongodb 更新嵌套数组的值[通俗易懂]概要 本文主要讲述在 mongodb 中,怎么更新嵌套数组的值。 使用$更新数组 测试 for (let i = 0; i < 3; i++) { let data = { name1_1: &

    2023-01-22
    163
  • pip安装的包的默认安装路径

    pip安装的包的默认安装路径pip是Python语言中最受欢迎的软件包安装工具之一,它可以帮助用户管理依赖关系和升级软件包。在使用pip安装软件包时,Python会按照不同的操作系统采用不同的默认路径进行安装。

    2024-09-22
    11
  • 数据库中的字符类型存储字符和汉字的数量相同_数据库中文本的数据类型

    数据库中的字符类型存储字符和汉字的数量相同_数据库中文本的数据类型sqlServer2012(936 简体中文GBK )为例: 例如: varchar(10),只能存储10个英文字符或数字,也只能存储5个汉字; char(10),只能存储10个英文字符或数

    2022-12-24
    144
  • Python Class:定义和使用类来组织代码

    Python Class:定义和使用类来组织代码Python是一种简单易学的编程语言。使用Python开发应用程序,涉及到的代码量往往比其他语言要少,提高了程序的开发效率。在Python中,类是一种非常重要而且强大的机制,它可以帮助开发者组织代码并且降低代码的重复性。类的使用方式类似于其他面向对象的编程语言,比如Java、C++等。

    2024-01-20
    100

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注