源码解析MySQL慢日志slow_log记录相关函数与逻辑

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
简介: 尝试源码分析MySQL慢日志slow_log的记录过程

源码解析MySQL慢日志记录相关函数与逻辑

操作环境

数据库版本:mysql-5.6.24 source code

操作系统版本:CentOS Linux release 7.6.1810 (Core)

以下主要函数的代码文件:

/mysql-5.6.24/sql/sql_class.h

/mysql-5.6.24/sql/sql_parse.cc

慢日志记录相关函数与逻辑

THD::update_server_status()函数

函数体

void update_server_status()

{

ulonglong end_utime_of_query= current_utime();

if (end_utime_of_query > utime_after_lock + variables.long_query_time)

server_status|= SERVER_QUERY_WAS_SLOW;

}

该函数主要进行慢查询的判断以及更新server_status,它判断慢查询的条件是:

end_utime_of_query > utime_after_lock + variables.long_query_time

其中:

end_utime_of_query来自于current_utime(),代码ulonglong end_utime_of_query= current_utime()进行了赋值。current_utime()是用来获取当前时间的,调用的是Linux操作系统(当前是linux系统)gettimeofday()函数。

utime_after_lock:这个值是指该SQL锁之后的时间,把SQL等待锁之后的时间作为真正的执行开始时间。假设SQL执行开始时间是1598532179899361(微秒),锁定时间假设进行了20秒(long_query_time为1秒)。而此处的utime_after_lock的值是1598532179899361+20秒的时间。这样变相的说明了,慢日志记录的SQL在进行耗时判断时,是不记录锁的(而MDL锁的时间是计算在内的,具体原因后面尝试讨论)。

variables.long_query_time:这是我们设置的long_query_time的时间。

所以这个函数主要是判断SQL的执行完成的时间加上long_query_time是否超过了当前时间(end_utime_of_query),以此判断是否是慢SQL,如果超过,则执行 server_status|= SERVER_QUERY_WAS_SLOW进行按位或且赋值运算(相当于server_status=server_status|SERVER_QUERY_WAS_SLOW),其中Mysql代码中定义SERVER_QUERY_WAS_SLOW值为2048,server_status是不确定的,会根据我们的SQL和使用方式进行赋值,我们测试时此处是2,则它们进行计算后,server_status值为2050(下面的log_slow_applicable()函数会用到)。

log_slow_statement()函数

函数体

void log_slow_statement(THD *thd)

{

DBUG_ENTER("log_slow_statement");

if (log_slow_applicable(thd))

log_slow_do(thd);

DBUG_VOID_RETURN;

}

update_server_status()函数判断完之后,继续执行,后面会执行log_slow_statement()函数,这个函数主要逻辑是:通过log_slow_applicable()判断该慢SQL是否可以记录到慢SQL里(具体逻辑下面说明),如果log_slow_applicable(thd)是true,则执行 log_slow_do(thd);

log_slow_applicable()函数

函数体

bool (THD *thd)

{

DBUG_ENTER("log_slow_applicable");

if (unlikely(thd->in_sub_stmt))

DBUG_RETURN(false); // Don't set time for sub stmt

if (thd->enable_slow_log)

{

bool warn_no_index= ((thd->server_status &

​ (SERVER_QUERY_NO_INDEX_USED |

​ SERVER_QUERY_NO_GOOD_INDEX_USED)) &&

​ opt_log_queries_not_using_indexes &&

​ !(sql_command_flags[thd->lex->sql_command] &

​ CF_STATUS_COMMAND));

bool log_this_query= ((thd->server_status & SERVER_QUERY_WAS_SLOW) ||

​ warn_no_index) &&

​ (thd->get_examined_row_count() >=

​ thd->variables.min_examined_row_limit);

bool suppress_logging= log_throttle_qni.log(thd, warn_no_index);

if (!suppress_logging && log_this_query)

DBUG_RETURN(true);

}

DBUG_RETURN(false);

}

该函数先判断是否开启了慢日志记录(if (thd->enable_slow_log)),如果开启,则进行如下判断:

一:判断warn_no_index是true还是false,主要判断:

​ 1, 使用thd->server_status和(SERVER_QUERY_NO_INDEX_USED|SERVER_QUERY_NO_GOOD_INDEX_USED)的值进行二进制的"位与&"运算,判断是否没有使用索引或者没有使用GOOD INDEX。其中SERVER_QUERY_NO_GOOD_INDEX_USED 值为16,SERVER_QUERY_NO_INDEX_USED值为32,thd->server_status前面已经计算过是2050。最终的运算式是:2050&(32|16),值为0。

​ 2,opt_log_queries_not_using_indexes 确认MySQL Server是否开启了log_queries_not_using_indexes参数。此处未开启,值为0。

​ 3,sql_command_flags[thd->lex->sql_command]和CF_STATUS_COMMAND)进行二进制的"位与&"运算,判断SQL的命令flag是否是CF_STATUS_COMMAND,然后进行"逻辑非 !"运算,其中sql_command_flags[thd->lex->sql_command]的值是变化的,会根据SQL类型不同而不同,此处由于我们使用的测试命令是show processlist,其值为4,CF_STATUS_COMMAND值为1U << 2(十进制是4)。最终的运算式是:4&4,值为4。进行"逻辑非 !"运算后是false。MySQL用这个条件来过滤admin_statements(slow log的参数log_slow_admin_statements)。只有当log_slow_admin_statements打开时,admin_statements进行比较时它才会为true。

最终的运算式是:0&&0&&false,值为false,这三个条件是"逻辑与&&"的关系,也就是三个条件必须全部是true,最终的warn_no_index才是true。

二:判断log_this_query是true还是false,主要判断:

​ 1,使用(thd->server_status & SERVER_QUERY_WAS_SLOW) 与warn_no_index的值进行"逻辑或||"运算。先对thd->server_status 和 SERVER_QUERY_WAS_SLOW两个的值进行二进制的"位与&"运算,其中thd->server_status值前面已经计算过为2050,SERVER_QUERY_WAS_SLOW值为2048,最终的运算式是:2050&2048,值为2048。然后再与warn_no_index进行"逻辑或||"运算,由于warn_no_index前面已经得出是false。2048||false的值为true。也就是这个表达式(thd->server_status & SERVER_QUERY_WAS_SLOW) ||warn_no_index)的值为true。

​ 2,使用thd->get_examined_row_count()与thd->variables.min_examined_row_limit比较,确认前者大于等于后者。这个主要是为了判断当前的慢查询的检索行数是否大于MySQL Server设置的变量min_examined_row_limit。由于我们测试时,min_examined_row_limit变量值并未进行设置,所以variables.min_examined_row_limit的值为0,此处表达式的结果为true。

最终的运算式是:true&&true,值为true。

三:判断是否要对慢日志进行suppress_logging,这个对应的MySQL server的log_throttle_queries_not_using_indexes 参数,由于我们测试时,其值并未设置,所以此处表达式的结果为false。

以上3个条件判断完成后,进行return的判断:

if (!suppress_logging && log_this_query)

DBUG_RETURN(true);

}

由于suppress_logging 值为false,所以!suppress_logging 值为true。log_this_query也是为true。最终条件成立,函数log_slow_applicable()函数返回true。

log_slow_do()函数

函数体

void log_slow_do(THD *thd)

{

DBUG_ENTER("log_slow_do");

THD_STAGE_INFO(thd, stage_logging_slow_query);

thd->status_var.long_query_count++;

if (thd->rewritten_query.length())

slow_log_print(thd,

​ thd->rewritten_query.c_ptr_safe(),

​ thd->rewritten_query.length());

else

slow_log_print(thd, thd->query(), thd->query_length());

DBUG_VOID_RETURN;

}

log_slow_applicable(thd)返回为true,进入log_slow_do()函数。该函数主要进行慢日志的写入操作。这个函数会调用slow_log_print()函数。

至此,一个SQL的慢日志判断过程完成。

目录
相关文章
|
7月前
|
Ubuntu 关系型数据库 MySQL
MySQL源码编译安装
本文详细介绍了MySQL 8.0及8.4版本的源码编译安装全过程,涵盖用户创建、依赖安装、cmake配置、编译优化等步骤,并提供支持多Linux发行版的一键安装脚本,适用于定制化数据库部署需求。
1972 4
MySQL源码编译安装
|
存储 Java 文件存储
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录—— logback.xml 配置文件解析
本文解析了 `logback.xml` 配置文件的详细内容,包括日志输出格式、存储路径、控制台输出及日志级别等关键配置。通过定义 `LOG_PATTERN` 和 `FILE_PATH`,设置日志格式与存储路径;利用 `&lt;appender&gt;` 节点配置控制台和文件输出,支持日志滚动策略(如文件大小限制和保存时长);最后通过 `&lt;logger&gt;` 和 `&lt;root&gt;` 定义日志级别与输出方式。此配置适用于精细化管理日志输出,满足不同场景需求。
3125 1
|
9月前
|
NoSQL 关系型数据库 MySQL
在Visual Studio Code中设置MySQL源码调试环境
以上步骤涵盖了在VS Code中设置MySQL源码调试环境的主要过程,是一个相对高级的任务,旨在为希望建立强大开发和调试环境的开发者提供指引。遵循这些步骤,将可以利用VS Code强大的编辑和调试功能来深入理解和改进MySQL数据库的底层实现。
621 0
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
1386 29
|
关系型数据库 MySQL PHP
源码编译安装LAMP(HTTP服务,MYSQL ,PHP,以及bbs论坛)
通过以上步骤,你可以成功地在一台Linux服务器上从源码编译并安装LAMP环境,并配置一个BBS论坛(Discuz!)。这些步骤涵盖了从安装依赖、下载源代码、配置编译到安装完成的所有细节。每个命令的解释确保了过程的透明度,使即使是非专业人士也能够理解整个流程。
463 18
|
监控 Java 应用服务中间件
Tomcat log日志解析
理解和解析Tomcat日志文件对于诊断和解决Web应用中的问题至关重要。通过分析 `catalina.out`、`localhost.log`、`localhost_access_log.*.txt`、`manager.log`和 `host-manager.log`等日志文件,可以快速定位和解决问题,确保Tomcat服务器的稳定运行。掌握这些日志解析技巧,可以显著提高运维和开发效率。
1639 13
|
监控 Shell Linux
Android调试终极指南:ADB安装+多设备连接+ANR日志抓取全流程解析,覆盖环境变量配置/多设备调试/ANR日志分析全流程,附Win/Mac/Linux三平台解决方案
ADB(Android Debug Bridge)是安卓开发中的重要工具,用于连接电脑与安卓设备,实现文件传输、应用管理、日志抓取等功能。本文介绍了 ADB 的基本概念、安装配置及常用命令。包括:1) 基本命令如 `adb version` 和 `adb devices`;2) 权限操作如 `adb root` 和 `adb shell`;3) APK 操作如安装、卸载应用;4) 文件传输如 `adb push` 和 `adb pull`;5) 日志记录如 `adb logcat`;6) 系统信息获取如屏幕截图和录屏。通过这些功能,用户可高效调试和管理安卓设备。
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
556 4
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
负载均衡 JavaScript 前端开发
分片上传技术全解析:原理、优势与应用(含简单实现源码)
分片上传通过将大文件分割成多个小的片段或块,然后并行或顺序地上传这些片段,从而提高上传效率和可靠性,特别适用于大文件的上传场景,尤其是在网络环境不佳时,分片上传能有效提高上传体验。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

推荐镜像

更多