MySQL 源码分析 v1.0

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/feivirus/article/details/83716680

第一节.mysql编译
参考
https://blog.jcole.us/innodb/
https://www.cnblogs.com/zengkefu/p/5674503.html
https://dev.mysql.com/doc/internals/en/getting-source-tree.html
https://www.cnblogs.com/-xep/p/8045213.html
https://www.jianshu.com/p/1cc29d893cfc
https://www.aliyun.com/jiaocheng/357678.html
https://www.cnblogs.com/songxingzhu/p/5713553.html
https://blog.csdn.net/yi247630676/article/details/80352655
https://blog.csdn.net/vipshop_fin_dev/article/details/79688717
http://blog.51cto.com/wangwei007/2300217?source=dra
http://ourmysql.com/archives/1090
http://www.orczhou.com/index.php/2012/11/mysql-innodb-source-code-optimization-1/
http://ourmysql.com/archives/1282
http://ourmysql.com/archives/1277
http://ourmysql.com/archives/1305
<深入理解mysql> 
<flex与bison>
https://dev.mysql.com/doc/internals/en/selects.html

cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DMYSQL_UNIX_ADDR=/tmp/mysql.sock -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_ARCHIVE_STORAGE_ENGINE=1 -DWITH_BLACKHOLE_STORAGE_ENGINE=1 -DWITH_PARTITION_STORAGE_ENGINE=1 -DENABLED_LOCAL_INFILE=1 -DMYSQL_USER=_mysql -DMYSQL_TCP_PORT=3306 -DMYSQL_DATADIR=/data/mysql -DDOWNLOAD_BOOST=1 -DWITH_BOOST=/Users/feivirus/Documents/software/boost

./mysqld --initialize-insecure --user=mysql --datadir=/data/mysql --explicit_defaults_for_timestamp=true

在/usr/local/mysql/bin/下面
sudo ./mysqladmin -uroot -pxxx shutdown 
在/usr/local/mysql/support-files/mysql.server下
sudo ./mysql.server start

第二节.mysql源码模块
一.mysql源码结构
(一)mysql-server根目录下:
1.client mysql命令行客户端工具
2.dbug 调试工具
3.Docs 一些说明文档
4.include 基本的头文件
5.libmysql 创建嵌入式系统的mysql客户端程序API
6.libmysqld mysql服务器的核心级API文件(8.0没了?)
7.mysql-test mysql的测试工具箱
8.mysys 操作系统API的大部分封装函数和各种辅助函数
9.regex 处理正则表达式的库
10.scripts 一些基于shell脚本的工具
11.sql 主要源代码
12.sql-bench 一些性能测试工具(8.0没了)
13.ssl一些ssl的工具和定义(8.0改为mysys_ssl) 
14.storage 插件式存储引起的代码
15.strings 各种字符串处理函数
16.support-files 各种辅助文件
17.vio 网络层和套接层的代码
18.zlib 数据压缩工具(8.0移到了utilities)

二.mysql启动
(一)mysql main()启动过程
(二)mysql select查询过程
如下图断点:

mysql 查询

依次需要经过连接处理,sql语法解析,sql优化,sql执行(存储引擎)四层处理.需要经过选取(限制),投影,连接处理.
依次经过的Sql接口(handle_connection()->do_command()->dispatch_command()),查询解析器(mysql_parse()->parse_sql()->yyparse()->mysql_execute_command()(为优化做准备)->handle_select()),查询优化器(join.prepare()->join.optimize()->join.exec()),查询执行器(do_select()->sub_select()->join.result.send_eof())

从栈底到栈底的每个方法阶段的功能简要描述:
sql连接接口阶段:
1.在mysqld.cc文件中,mysql启动后,进入mysqld_main().在该main()方法中进入setup_conn_event_handler_threads().通过socket_conn_event_handler()进入socket_conn_event_handler(),在该方法中死循环调用connection_event_loop(),进入Connection_handler_manager::process_new_connection(),进入add_connection(). 在文件connection_hanlder_per_thread.cc中通过Per_thread_connection_handler::add_connection()方法,调用mysql_thread_create创建线程,线程的运行函数为connection_handler_per_thread.cc文件中的handle_connection().
2.在handle_connection中,通过while循环调用sql_parse.cc中的do_command(),循环的作用是从用户输入的多个命令中依次选择一个命令去执行.在do_command()中,调用Protocol_classic::get_command(),进入Protocol_classic::parse_packet()读取一个数据包,解析为command命令,command命令有COM_QUERY、COM_FIELD_LIST、COM_STMT_EXECUTE等。在在do_command()中继续调用sql_parse()文件中的dispatch_command().在dispatch_command()中包含了各种类型的command怎么处理的switch case。比如select的进入case COM_QUERY.先alloc_query()复制线程线程过来的查询命令。再进入sql_parse.cc文件中的mysql_parse()方法,开始sql语法解析.

sql语法解析阶段:
3.在mysql_parse()中,主要完成三个工作。检查查询缓存里是否有以前执行过的某个查询。调用词法解析器(通过parse_sql()方法),调用查询优化器(通过mysql_execute_command()方法).
4.依次进入sql_parse.cc中的parse_sql(),进入sql_class.cc中的THD::sql_parser(),进入MYSQLparse().MYSQLparse是个宏定义,为#define yyparse MYSQLparse.这时进入yacc的sql_yacc.cc文件中的语法解析器函数yyparse()开始语法解析.解析器基于sql_yacc.yy文件通过yacc生成.Mysql的词法解析器没有用flex,自己编写的。语法解析器用的Bison.
5.在mysql_parse()中继续调用sql_parse()文件中的mysql_execute_command()方法,为sql优化做准备.在该方法中,通过switch case为各种sql命令优化做准备.如果是select查询,进入case SQLCOM_SELECT,进入Sql_cmd_dml::execute()方法.在execute()方法中,先调用precheck()查询是否有权限,然后调用open_tables_for_query()打开要查询的表,然后调用lock_tables()锁定相关的表,然后调用Sql_cmd_dml::execute_inner(),进入真正的查询优化.

sql查询优化阶段:
在Sql_cmd_dml::execute_inner()方法中,先进入sql_optimizer.cc文件中的JOIN::optimize(),再进入sql_executor.cc文件中的JOIN::exec()开始查询执行器。

sql查询执行器:
在sql_executor.cc文件中的JOIN::exec()中.先调用send_result_set_metadata()生成查询标题,再进入sql_executor.cc中的do_select()生成查询结果.在do_select()中调用sub_select().在sub_select()中,先判断是否是最后一条记录.然后while循环每次读取一条记录。

第三节.存储引擎,函数,命令
一.关键类
sql/handler.h下的struct handlerton,class handler,比如选择inno引擎,create table时进入storage/innobase/handler/ha_innobase::create()方法.insert into语句时进入ha_innobase::write_row().
二.用户自定义函数
调用命令create function,实现函数的so库。参考例子在sql/udf_example.cc中。
三.本机函数
修改代码在lex.h,item_create.cc,item_str_func.cc中。常用函数都在lex.h中。
四.新的sql命令
修改sql_yacc.yy,sql_parse.cc.所有系统支持的命令在enum_sql_command枚举类中。命令处理在sql_parse.cc的mysql_execute_command()中。

第四节.SQL接口

第五节.查询解析器(Lex-YACC/Bison)
一.词法分析
在sql_yacc.cc文件中定义
#define yyparse MYSQLparse
#define yylex   MYSQLlex
#define yyerror MYSQLerror
#define yylval  MYSQLlval
所以词法分析在sql_lex.cc的MYSQLlex()方法中.调用堆栈如图

进入lex_one_token(),在find_keyword()中调用Lex_hash::get_hash_symbol()查找关键字.关键字是根据lex.h中的symbols[]数组调用gen_lex_hash.cc生成lex_hash.h中的符号数组sql_keywords_and_funcs_map[].词法分析解析到SELECT后,执行find_keyword去找是否是关键字,发现SELECT是关键字,于是给yacc返回SELECT_SYM用于语法分析。note:如果我们想要加关键字,只需在sql_yacc.yy上面添加一个%token xxx,
然后在lex.h里面加入相应的字符串和SYM的对应即可。依次取出token,比如sql语句为"select * from user",token依次为select,*,from,user,返回的为token的id号.*的id号
为42.这个id号在sql_yacc.h中定义,比如#define SELECT_SYM 748.已经分析和没有分析过的语句都放在Lex_input_stream结构的lip变量中.在lex_one_token()依次判断读入的符号,解析状态,比如"select @@version_comment limit 1;"语句,进入case MY_LEX_SYSTEM_VAR状态,解析MY_LEX_IDENT_OR_KEYWORD,就是"version_comment".


二.语法分析
入口为sql_yacc.cc文件中的yyparse()方法.
语法分析文件在sql_yacc.yy中.以"select @@version_comment"为例。进入lex_one_token()方法中的case MY_LEX_SYSTEM_VAR后.
(一).Item对象
可以是一个文本字符串/数值对象,表的某一列(例如,select c1,c2 from dual…中的c1,c2),一个比较动作,例如c1>10,一个WHERE子句的所有信息.
(二).mysql语法树处理过程

在sql_yacc.yy中,如果是select查询语句,进入query处理逻辑。具体格式如下
query:

| verb_clause END_OF_INPUT
          {
            /* Single query, not terminated. */
            YYLIP->found_semicolon= NULL;
          }
        ;

select:
          select_init
          {
            LEX *lex= Lex;
            lex->sql_command= SQLCOM_SELECT;
          }
        ;
如果是where语句的,处理如下
where_clause:
          /* empty */  { Select->where= 0; }
        | WHERE
          {
            Select->parsing_place= IN_WHERE;
          }
          expr
          {
            SELECT_LEX *select= Select;
            select->where= $3;
            select->parsing_place= NO_MATTER;
            if ($3)
              $3->top_level_item();
          }
        ;
以select * from users where id = 1 语句为例,

生成的语法树大致如下左图,右图为mysql内部的存储结构.

以select * from user where id = 1为例,每次读入一个token,要考虑移进还是规约.通过yylex()方法得到的yychar是id号,该id号数字在sql_yacc.h中定义.
1.lex_one_token,读入"select"->select_sym token->规约到sql_yacc.yy 的select_part2:

2.读入*号,依次规约到sql_parse.yy中的select_item_list:

3.读入from,依次规约到sql_parse.yy中的select_part2:

4.读入"user",依次规约到table_factor:->keyword_sp:->keyword:->ident:

5.读入"where",依次规约到simple_ident_q:->opt_use_partition:->opt_table_alias:->opt_key_definition:->table_factor:->

join_table:->esc_table_ref:->derived_table_list:->join_table_list:->where_clause:->IDENT_sys

6.读入"=" 

。。。
sql语法树的状态切换过程如下,数字为状态转换表的编号,编号顺序为上面左图从树叶到树根的语法解析过程,括号内为bison规约的mysql中的非终结符:
1127(select_part2),1137,读入*,1149(select_item_list),读入from,1128(select_part2),读入user,1444(table_factor),2326(keyword_sp),1988(keyword,只是匹配到,没有动作),1979(ident),读入where,1966(simple_ident_q),1441(opt_use_partition),1518(opt_table_alias),1480(opt_key_definition),1477,1481,1445(table_factor),1421(join_table),1412,1415(esc_table_ref),1417(derived_table_list),1414(join_table_list),
1523(where_clause),1973(IDENT_sys),读入=号,1978(ident),1955(simple_ident),1223,1206,1191,1177,1215(comp_op),读入数字1,1942(NUM_literal),1933(literal),1229,读入end_of_input,1206,1191,1175(,创建eq_creator),1171,1524(where_clause),1530,1525,1541,1552(opt_limit_clause),1588,1135(select_from),1132,1144,1129,1125(select_init2),2548(union_clause),1126,1119,1118(select),50,8,5(query).

(三).Mysql语法树存储结构

以select * from user where id = 1为例,如上图右侧图所示.Mysql语句中的*为投影的列,存储在thd->main_lex.current_select->item_list中,列表中的每一项为一个list_node. where条件存储在thd->main_lex.current_select->where对象中。where为级联结构.

处理过程如下,比如处理到"="时的,堆栈如下:

mysql为"="创建一个Eq_creator类标识,如前面图所示,它存储在thd->main_lex.current_select->where对象中.where类型为

item_func_eq类.它依次继承Item_bool_rowready_func2,Item_bool_fun2,如下图所示

在Item_bool_fun2类中有cmp成员,类型为Arg_comparator,如下图

存储了=运算符的两边的表达式.

第六节.查询优化器
一.索引
表的索引存储在 KEY *TABLE::key_info数组中.KEY在class KEY中定义,其中的KEY_PART_INFO *key_part指向每一个索引列。索引种类存储在my_base.h中,比如#define HA_FULLTEXT 128.
入口JOIN::optimize()

第七节。查询执行处理器

第八节。缓存和缓冲区

第九节。高性能索引.

第十节.主从复制

第十一节.备份与恢复

猜你喜欢

转载自blog.csdn.net/feivirus/article/details/83716680