如何进行MySQL源码调试(一条select语句的执行流程)

一、背景

MySQL是当今世上最受欢迎的使用最广泛的开源数据库,它的繁荣离不开它的开源特性。放在过去商业数据库的时代,大家都没有机会接触到数据库的源代码,但在如今开源数据库的时代,越来越多的人开始研究数据库的源码,并给社区贡献代码,MySQL官方每次发布新版本都要感谢一些在社区上贡献代码的程序员。现在新的数据库时代也给DBA提出了更高的要求,学会调试源码,通过源码定位问题,这是DBA进阶的方向。MySQL的源码有几百上千万行,想全部搞懂几乎是不可能的,研究源码一般推荐从某个功能点入手。而学会调试源码,不管对研究源码或通过源码定位问题,都是必备的技能。本文将介绍Linux平台下如何通过gdb进行MySQL源码调试,并简单介绍通过调试源码定位一条select语句的执行流程。

二、源码调试方法

关于源码的编译及调试,不同的平台可以通过不同的工具来进行:windows平台下可以通过visual studio来进行,https://www.cnblogs.com/huangxincheng/p/13084736.html 按照这篇文章的方法可以进行5.7的调试,通过cmake生成mysql.sln文件,然后给打开sln文件编译成功后,就可以进入调试模式了。在某个函数处打下断点,F5进入调试模式后,就可以对数据库进行操作了,命中断点后就可以查看堆栈的函数调用关系。一般在实际应用中,MySQL都是运行在Linux平台下,在Linux平台下一般是通过GDB(GNU symbolic debugger)工具进行调试,C/C++ 项目的开发和调试包括故障排查都是利用 GDB 完成的。此外,VsCODE这种IDE工具可以在本地的windows操作系统下,通过ssh远程调试Linux平台下的MySQL,https://mp.weixin.qq.com/s/RO_Ipa9_SH8_DuVholrgvg,姜老师这篇文章也简单讲了一下如何操作。

三、GDB调试MySQL源码

3.1 MySQL源码下载

首先需要在MySQL官网上下载MySQL源码,操作系统选择为source code,OS版本选择为ALL OPERATING SYSTEM,下载带boost头文件的源码包。如果对MySQL的版本没有特别要求的话,一般推荐下载最新版本的,因为老版本中存在bug的概率较大,编译过程需要解决这些bug,比如在8.0.23版本中编译过程中报了这个错:buf0buf.cc:1227:44: error: ‘SYS_gettid’ was not declared in this scope。参考MySQL官方论坛:https://forums.mysql.com/read.php?117,674410,676378#msg-676378,在storage/innobase/buf/buf0flu.cc文件代码中加上声明#include <sys/syscall.h>,解决了这个报错。

3.2 编译安装环境准备

源码编译需要gcc等基础软件支持,MySQL8.0版本 gcc至少需要7.1以上,一般推荐直接升级到最高版本,在线环境比较好办直接通过yum安装即可。离线环境的下载及安装可以参考这两篇文章分别升级gcc和cmake:

https://mp.weixin.qq.com/s/kWPD1o5fWTAxUNETGsXA7ghttps://mp.weixin.qq.com/s/5sHPJ0Kxkmj2llW-UvEbkg把gcc和cmake搞定后,就可以开始编译安装mysql了。

3.3 编译和安装MySQL

参考官方文档通过源码安装mysql这一章可以完成mysql的编译和安装:

如何进行MySQL源码调试(一条select语句的执行流程)

实际cmake加上这几个参数,其中-DWITH_DEBUG=1是为了开启调试模式。

cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DWITH_BOOST=/root/gdb_mysql/mysql-8.0.23/boost/boost_1_73_0 -DWITH_DEBUG=1

接着make install成功后,配置好配置文件/etc/my.cnf,就可以初始化数据库并启动数据库了。

mysqld --initialize --user=mysql
mysqld_safe --user=mysql &

启动完数据库后,登录数据库可以发现现在已经是debug模式了。

如何进行MySQL源码调试(一条select语句的执行流程)

3.4 gdb调试源码

完成MySQLdebug版本的安装和启动后,gdb命令下attach mysql的进程号,就可以对mysql进程进行打断点调试了。

如何进行MySQL源码调试(一条select语句的执行流程)

 gdb调试过程中常用的命令可以参考如下:

attach 进程号 #进入调试模式
b Sql_cmd_insert::mysql_insert  #在某个函数打下断点
b filename:linenum  #在文件的某行打下断点    
clear function  #在某个函数处删除断点
bt   #查看堆栈信息
n    #next 单步调试,每次只执行往下一行代码,对于调用的函数来说,next 命令只会将其视作一行代码。 #n 3 往下执行三行代码 
s    #step 单步调试,当 step 命令所执行的代码行中包含函数时,会进入该函数内部,并在函数第一行代码处停止执行。
c    #continue 当程序在某一断点处停止运行后,使用该指令可以继续执行,直至遇到下一个断点或者程序结束。
l    #list 显示源程序代码的内容,包括各行代码所在的行号。
p xxx  #print 打印指定xxx变量的值
info breakpoint   #查看断点信息

 

四、一条select语句的调试

在handle_connection函数处打下断点,然后在mysql客户端执行一条select语句,可以在gdb里面看到,程序很快命中了断点,接下来便可以通过单步调试,一步一步定位select语句的执行流程。

#客户端连接的线程处理函数
handle_connection (arg=0xb998240) at /gdb/mysql-8.0.23/sql/conn_handler/connection_handler_per_thread.cc
#读取连接发来的命令,然后执行
do_command (thd=0x7f5a3815d3c0) at /gdb/mysql-8.0.23/sql/sql_parse.cc:1320
#THD类,描述每个客户端连接产生的后台进程
#发出命令
dispatch_command (thd=0x7f5a3815d3c0, com_data=0x7f5afc7adb00, command=COM_QUERY)
#根据command=COM_QUERY,调用alloc_query函数(读取查询语句并存在thd->query中)
if (alloc_query(thd, com_data->com_query.query,com_data->com_query.length))
#执行到dispatch_sql_command(thd, &parser_state); 解析sql语句,然后把结果发给executor
dispatch_sql_command(thd, &parser_state);
#进入dispatch_sql_command函数,执行到mysql_execute_command函数(Execute command saved in thd and lex->sql_command)
error = mysql_execute_command(thd, true);

#在mysql_execute_command函数中,执行到case SQLCOM_SELECT,res = lex->m_sql_cmd->execute(thd),进入到execute函数
#在mysql_execute_command函数中,switch (lex->sql_command)通过case SQLCOM_XXX,转到不同语句的执行器
#这时候就进入到了lex的公共属性m_sql_cmd类下面的execute函数;
#通过单步调试,此时程序进入到了Sql_cmd_dml::execute (this=0x7f5a38bee0b0, thd=0x7f5a3815d3c0) at /gdb/mysql-8.0.23/sql/sql_select.cc:517

#此时可以看到,解析SQL是在dispatch_sql_command和mysql_execute_command函数中完成的,Sql_cmd_dml::execute的函数主要有6步
Prelocking;Preparation;Locking of tables;Optimization;Execution or explain;Cleanup
#lock_tables(thd, lex->query_tables, lex->table_count, 0) 锁表
#execute_inner(thd) 执行

#进入execute阶段
Sql_cmd_dml::execute_inner (this=0x7f1ca0011858, thd=0x7f1ca0005ed0) at /gdb/mysql-8.0.23/sql/sql_select.cc:809
if (unit->optimize(thd, /*materialize_destination=*/nullptr, /*create_iterators=*/true)) #优化
if (unit->execute(thd)) return true; #执行
#此时执行到了SELECT_LEX_UNIT::execute (this=0x7f1ca0023e48, thd=0x7f1ca0005ed0) at /gdb/mysql-8.0.23/sql/sql_union.cc:1267
return ExecuteIteratorQuery(thd);
#执行ExecuteIteratorQuery这个函数
SELECT_LEX_UNIT::ExecuteIteratorQuery (this=0x7f1ca0023e48, thd=0x7f1ca0005ed0) at /gdb/mysql-8.0.23/sql/sql_union.cc:1125
#执行完成后返回查询语句的结果值 return query_result->send_eof(thd);
#这个时候Sql_cmd_dml::execute_inner函数也执行完成了,进而Sql_cmd_dml::execute,mysql_execute_command也接着执行完成

#执行到dispatch_command函数的thd->send_statement_status(); 这一行,看到客户端执行的查询语句也输出了结果
#执行到handle_connection的while (thd_connection_alive(thd)) {if (do_command(thd)) break;}; 意味着mysql连接结束了,这时候调试也随之结束。

select语句的执行流程可以总结如下,这些函数可以方便以后打断点,更快的定位问题:

1. 客户端连接线程处理函数
handle_connection (arg=0xb998240) at /gdb/mysql-8.0.23/sql/conn_handler/connection_handler_per_thread.cc:301
2. 读取连接发来的命令,然后执行
do_command (thd=0x7f5a3815d3c0) at /gdb/mysql-8.0.23/sql/sql_parse.cc:1320
3. 发出命令,并将查询语句存在thd->query中
dispatch_command (thd=0x7f1ca0011100, com_data=0x7f1d644d3b00, command=COM_QUERY) at /gdb/mysql-8.0.23/sql/sql_parse.cc:1836
4. 解析sql语句,然后把结果发给executor
dispatch_sql_command (thd=0x7f1ca0011100, parser_state=0x7f1d644d2a60) at /gdb/mysql-8.0.23/sql/sql_parse.cc:4988
5. 执行存在thd中的语句
mysql_execute_command (thd=0x7f1ca0011100, first_level=true) at /gdb/mysql-8.0.23/sql/sql_parse.cc:4407
6. SELECT语句的:准备,锁表,优化,执行
Sql_cmd_dml::execute (this=0x7f1ca09feb28, thd=0x7f1ca0011100) at /gdb/mysql-8.0.23/sql/sql_select.cc:612
7. SELECT语句的优化和执行
Sql_cmd_dml::execute_inner (this=0x7f1ca0011858, thd=0x7f1ca0005ed0) at /gdb/mysql-8.0.23/sql/sql_select.cc:809
8. SELECT语句的执行
SELECT_LEX_UNIT::execute (this=0x7f1ca0023e48, thd=0x7f1ca0005ed0) at /gdb/mysql-8.0.23/sql/sql_union.cc:1267
9. 执行语句,返回结果
SELECT_LEX_UNIT::ExecuteIteratorQuery (this=0x7f1ca0023e48, thd=0x7f1ca0005ed0) at /gdb/mysql-8.0.23/sql/sql_union.cc:1125

五、总结

不管是研究MySQL源码还是通过源码定位问题,学会调试MySQL源码都是必备的基础技能,MySQL源码体系十分庞大,调试源码可以更快更清晰从源码中定位问题。

原文链接:https://www.cnblogs.com/coygfly/p/17379482.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:如何进行MySQL源码调试(一条select语句的执行流程) - Python技术站

(0)
上一篇 2023年5月10日
下一篇 2023年5月11日

相关文章

  • MySQL事务(transaction)看这篇就足够了

    MySQL事务(transaction)是数据库管理中的一种重要机制,可以防止数据在并发读写时出现异常情况,保证数据的一致性、可靠性和完整性。下面是“MySQL事务(transaction)看这篇就足够了”的详细攻略: 什么是事务 事务是一组操作,这组操作被当成一个整体来看待,要么全部执行成功,要么全部执行失败,不能出现部分执行的情况。如果事务的所有操作都执…

    MySQL 2023年5月18日
    00
  • MYSQL SQL查询近7天,一个月的数据

      //今天 select * from 表名 where to_days(时间字段名) = to_days(now()); //昨天 SELECT * FROM 表名 WHERE TO_DAYS( NOW( ) ) – TO_DAYS( 时间字段名) <= 1 //近7天 SELECT * FROM 表名 where DATE_SUB(CURDATE…

    MySQL 2023年4月27日
    00
  • 【数据库】7.0 MySQL入门学习(七)——MySQL基本指令:帮助、清除输入、查询等

    1.0 help == ? 帮助指令,查询某个指令的解释、用法、说明等。详情参考博文: 【数据库】6.0 MySQL入门学习(六)——MySQL启动与停止、官方手册、文档查询  https://www.cnblogs.com/xiaofu007/p/10301005.html  2.0 在cmd命令行中,输入“”ysql –help”,可以查看由mysql…

    MySQL 2023年4月12日
    00
  • 对于MySQL数据库四种隔离等级

    对于MySQL事务有四种隔离级别,分别是以下四种: 1.读未提交 2.读提交 3.可重复读 4.串行化(加锁) 对于隔离我们都是说在并发的情况下发生的事情,读取的数据在并发的情况下会发生什么情况。 并且我们知道所有的事务都是原子性操作。但是在这些事务中隔离等级不一样,并发的速度和安全情况都是不一样的。等级越高,速度越慢但越安全。 1.读未提交:表示根本什么措…

    MySQL 2023年4月12日
    00
  • MySQL如何创建并执行事件?

    MySQL事件是可以预定义、独立运行的MySQL代码块。您可以使用MySQL事件来定期执行维护操作,如清除过期数据、备份数据等等。MySQL事件可以在MySQL服务器上创建和执行。 以下是创建和执行MySQL事件的详细步骤: 1.创建一个MySQL事件: CREATE EVENT my_event ON SCHEDULE EVERY 1 DAY DO BEG…

    MySQL 2023年3月10日
    00
  • 数据库系统原理之数据管理技术的发展

    数据管理技术的发展 第一节 数据库技术发展概述 数据模型是数据库系统的核心和基础 以数据模型的发展为主线,数据库技术可以相应地分为三个发展阶段: 第一代的网状、层次数据库系统 第二代的关系数据库系统 新一代的数据库系统 一、第一代数据库系统 层次数据库系统 层次模型 网状数据库系统 网状模型 层次模型是网状模型的特例 第一代数据库系统有如下两类代表: 196…

    MySQL 2023年4月17日
    00
  • 解决pymysql cursor.fetchall() 获取不到数据的问题

    下面我将详细讲解如何解决使用pymysql时,cursor.fetchall()获取不到数据的问题。 问题描述 在使用pymysql访问MySQL数据库时,我们通常需要使用cursor对象进行操作,例如执行SQL查询等。在执行查询并调用 cursor.fetchall() 方法获取所有结果时,有时会出现返回空结果的情况,即使数据库中确实存在符合条件的数据。出…

    MySQL 2023年5月18日
    00
  • MySQL执行SQL语句的流程详解

    MySQL 执行 SQL 语句的流程详解 MySQL 是一种开源的,常见的关系型数据库管理系统。SQL (Structured Query Language)是一种用于管理关系型数据库的语言。在进行数据库操作时,我们需要编写 SQL 语句,MySQL 会根据 SQL 语句的执行计划实现数据的存储和管理。 MySQL 执行 SQL 语句的步骤 1. 连接数据库…

    MySQL 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部