第一段:背景
MySQL是当今世上最受欢迎的使用最广泛的开源数据库,它的繁荣离不开它的开源特性.放在过去商业数据库的时代,大家都没有机会接触到数据库的源代码,但在如今开源数据库的时代,越来越多的人开始研究数据库的源码,并给社区贡献代码,MySQL官方每次发布新版本都要感谢一些在社区上贡献代码的程序员.现在新的数据库时代也给DBA提出了更高的要求,学会调试源码,通过源码定位问题,这是DBA进阶的方向.MySQL的源码有几百上千万行,想全部搞懂几乎是不可能的,研究源码一般推荐从某个功能点入手.而学会调试源码,不管对研究源码或通过源码定位问题,都是必备的技能.本文将介绍Linux平台下如何通过gdb进行MySQL源码调试,并简单介绍通过调试源码定位一条select语句的执行流程.
第二段:源码调试方法
第三段:GDB调试MySQL源码
参考官方文档通过源码安装mysql这一章可以完成mysql的编译和安装:
实际cmake加上这几个参数,其中-DWITH_DEBUG=1是为了开启调试模式.
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DWITH_BOOST=/root/gdb_mysql/mysql-⑧0.23/boost/boost_1_73_0 -DWITH_DEBUG=1
接着make install成功后,配置好配置文件/etc/my.cnf,就可以初始化数据库并启动数据库了.
mysqld --initialize --user=mysql
mysqld_safe --user=mysql
启动完数据库后,登录数据库可以发现现在已经是debug模式了.
完成MySQLdebug版本的安装和启动后,gdb命令下attach mysql的进程号,就可以对mysql进程进行打断点调试了.
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 (arg=0xb998240) at /gdb/mysql-⑧0.23/sql/conn_handler/connection_handler_per_thread.cc #读取连接发来的命令,然后执行 do_command (thd=0x7f5a3815d3c0) at /gdb/mysql-⑧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-⑧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-⑧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-⑧0.23/sql/sql_union.cc:1267 return ExecuteIteratorQuery(thd); #执行ExecuteIteratorQuery这个函数 SELECT_LEX_UNIT::ExecuteIteratorQuery (this=0x7f1ca0023e48, thd=0x7f1ca0005ed0) at /gdb/mysql-⑧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-⑧0.23/sql/conn_handler/connection_handler_per_thread.cc:301 2. 读取连接发来的命令,然后执行 do_command (thd=0x7f5a3815d3c0) at /gdb/mysql-⑧0.23/sql/sql_parse.cc:1320 3. 发出命令,并将查询语句存在thd->query中 dispatch_command (thd=0x7f1ca0011100, com_data=0x7f1d644d3b00, command=COM_QUERY) at /gdb/mysql-⑧0.23/sql/sql_parse.cc:1836 4. 解析sql语句,然后把结果发给executor dispatch_sql_command (thd=0x7f1ca0011100, parser_state=0x7f1d644d2a60) at /gdb/mysql-⑧0.23/sql/sql_parse.cc:4988 5. 执行存在thd中的语句 mysql_execute_command (thd=0x7f1ca0011100, first_level=true) at /gdb/mysql-⑧0.23/sql/sql_parse.cc:4407 6. SELECT语句的:准备,锁表,优化,执行 Sql_cmd_dml::execute (this=0x7f1ca09feb28, thd=0x7f1ca0011100) at /gdb/mysql-⑧0.23/sql/sql_select.cc:612 7. SELECT语句的优化和执行 Sql_cmd_dml::execute_inner (this=0x7f1ca0011858, thd=0x7f1ca0005ed0) at /gdb/mysql-⑧0.23/sql/sql_select.cc:809 8. SELECT语句的执行 SELECT_LEX_UNIT::execute (this=0x7f1ca0023e48, thd=0x7f1ca0005ed0) at /gdb/mysql-⑧0.23/sql/sql_union.cc:1267 9. 执行语句,返回结果 SELECT_LEX_UNIT::ExecuteIteratorQuery (this=0x7f1ca0023e48, thd=0x7f1ca0005ed0) at /gdb/mysql-⑧0.23/sql/sql_union.cc:1125
第五段:总结
不管是研究MySQL源码还是通过源码定位问题,学会调试MySQL源码都是必备的基础技能,MySQL源码体系十分庞大,调试源码可以更快更清晰从源码中定位问题.
以上就是土嘎嘎小编为大家整理的如何进行MySQL源码调试_一条select语句的执行流程)相关主题介绍,如果您觉得小编更新的文章只要能对粉丝们有用,就是我们最大的鼓励和动力,不要忘记讲本站分享给您身边的朋友哦!!