mysql show processlist Time为负数的思考

简介: mysql show processlist Time为负数的思考

一、问题来源

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

image.png


二、关于show processlist中的Time

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

  1. Percona
  2. time_t now= my_time(0);
  3. protocol->store_long ((thd_info->start_time > now) ? 0
  4. : (longlong) (now - thd_info->start_time));
  5. 官方版:
  6. time_t now= my_time(0);
  7. protocol->store_long ((longlong) (now - thd_info->start_time));


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


三、计算方式解读和测试

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

重载1

  1. inline void set_time()
  2. {
  3. start_utime= utime_after_lock= my_micro_time();
  4. if (user_time.tv_sec || user_time.tv_usec)
  5. {
  6. start_time= user_time;
  7. }
  8. else
  9. my_micro_time_to_timeval(start_utime, &start_time);
  10. ...
  11. }

重载2


  1. inline void set_time(const struct timeval *t)

  2. {

  3. start_time= user_time= *t;

  4. start_utime= utime_after_lock= my_micro_time();

  5. ...

  6. }



重载3


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为命令开始执行的时间如下:

  1. 堆栈:
  2. query event

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

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

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

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

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

  1. 堆栈:
  2. query event

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

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

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

  1. 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很大的情况,如下设置:

  1. 主库:
  2. [root@mysqltest2 test]# date
  3. Fri Nov 1 01:40:54 CST 2019

  4. 从库:
  5. [root@gp1 log]# date
  6. Tue Nov 19 15:58:37 CST 2019

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

image.png

3、设置timestamp

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

  1. mysql> set timestamp=1572540000

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

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

  1. thd->set_time(&tmp);

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

image.png

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的时候这个时间和主库基本一致,如下:

image.png

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

Query Event的exetime

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

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

  3. 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也会出现异常如下:image.png

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

            </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;
相关文章
|
机器学习/深度学习 调度
详解 Diffusion (扩散) 模型
详解 Diffusion (扩散) 模型
1098 0
|
人工智能 文字识别 监控
将人工智能融入多媒体 助力视频产业加速——阿里云视频AI全能力解读
结合人工智能视频理解流程和用户的需求场景,我们将视频AI的功能分成四个大部分,视频智能审核、视频内容理解、视频智能编辑、视频版权保护。其中视频审核功能包括视频鉴黄、暴恐涉政识别、广告二维码识别、无意义直播识别等,利用识别能力将网络上没营养和不健康的视频内容进行排查和处理;视频理解功能包括视频分类、标签,人物识别、语音识别,同时也包括对视频中的文字进行识别(OCR);视频编辑层面可以实现视频首图、视频摘要、视频highlight的生成,同时支持新闻拆条;关于视频版权,支持视频相似性、同源视频检索和音视频指纹等功能。
17791 0
将人工智能融入多媒体 助力视频产业加速——阿里云视频AI全能力解读
|
机器学习/深度学习 算法 openCL
高效、轻量的深度学习框架MNN
MNN是一个高效、轻量的深度学习框架。
高效、轻量的深度学习框架MNN
|
缓存 网络协议 安全
【计算巢】DNS 解析过程详解:域名如何转换为 IP 地址
【5月更文挑战第31天】DNS(域名系统)将人类可读的域名转换为IP地址,涉及本地DNS缓存、层次化DNS服务器系统,包括根DNS、顶级域名DNS和权威DNS。当查询域名时,通过DNS服务器间的交互找到对应IP并返回给浏览器。Python示例展示了DNS查询过程。尽管DNS面临安全挑战,如欺骗和缓存中毒,采取安全措施可确保其稳定性和安全性。它是互联网的重要基础,连接域名与IP,支持便捷的网络访问。
631 0
|
11月前
|
Linux Docker 容器
Linux 中停止 Docker 服务报 warning 导致无法彻底停止问题如何解决?
在 Linux 系统中,停止 Docker 服务时遇到警告无法彻底停止的问题,可以通过系统管理工具停止服务、强制终止相关进程、检查系统资源和依赖关系、以及重置 Docker 环境来解决。通过以上步骤,能够有效地排查和解决 Docker 服务停止不彻底的问题,确保系统的稳定运行。
821 19
|
人工智能 缓存 安全
阿里云服务器实例规格性能参考:从五代到八代及经济型e与通用算力型u1
阿里云不断推出新一代的云服务器实例规格,以满足不同用户的多样化需求。本文将介绍阿里云服务器从五代到八代的实例规格,以及经济型e和通用算力型u1这两种热门实例规格,帮助用户更好地选择适合自己的云服务器。
阿里云服务器实例规格性能参考:从五代到八代及经济型e与通用算力型u1
|
搜索推荐 前端开发 开发者
「Mac畅玩鸿蒙与硬件19」鸿蒙UI组件篇9 - 自定义动画实现
自定义动画让开发者可以设计更加个性化和复杂的动画效果,适合表现独特的界面元素。鸿蒙提供了丰富的工具,支持通过自定义路径和时间控制来创建复杂的动画运动。本篇将带你学习如何通过自定义动画实现更多样化的效果。
476 11
「Mac畅玩鸿蒙与硬件19」鸿蒙UI组件篇9 - 自定义动画实现
|
数据可视化 搜索推荐 数据挖掘
2024年备受推荐的数据可视化平台——团队管理必备利器
在数字化时代,数据已成为决策、管理和运营的核心。数据可视化平台通过直观的图表形式,帮助企业和团队高效解读和分析数据,提升项目管理、团队绩效评估、资源分配、销售与市场分析等方面的能力。2024年推荐的五大数据可视化平台包括板栗看板、Tableau、PowerBI、QlikView和Looker,各具特色,适用于不同场景和需求。板栗看板操作简便,适合团队协作;Tableau功能强大,适合大型企业;PowerBI与微软生态系统无缝集成;QlikView擅长处理海量数据;Looker则专注于数据探索和商业智能。希望本文能帮助用户选择合适的可视化平台。
2126 2
2024年备受推荐的数据可视化平台——团队管理必备利器
|
存储 Linux Windows
在Linux中,如何查看linux中内存使用率最高的进程?
在Linux中,如何查看linux中内存使用率最高的进程?
|
存储 SQL 数据库
深入浅出后端开发之数据库优化实战
【10月更文挑战第35天】在软件开发的世界里,数据库性能直接关系到应用的响应速度和用户体验。本文将带你了解如何通过合理的索引设计、查询优化以及恰当的数据存储策略来提升数据库性能。我们将一起探索这些技巧背后的原理,并通过实际案例感受优化带来的显著效果。
234 4