加入收藏 | 设为首页 | 会员中心 | 我要投稿 宁德站长网 (https://www.0593zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

研究MYSQL源代码–SQL历险记

发布时间:2021-12-22 17:21:43 所属栏目:大数据 来源:互联网
导读:当客户端连接上MySQL服务端之后,发出请求之前,服务端的线程是阻塞在do_command(sql/parse.cc)里的my_net_read函数中(就是socket里的read). 当客户端键入sql语句(本文例子select * from zzz)发送到服务端之后, my_net_read返回, 并从tcpbuffer中读取数据写入到
当客户端连接上MySQL服务端之后,发出请求之前,服务端的线程是阻塞在do_command(sql/parse.cc)里的my_net_read函数中(就是socket里的read).
 
当客户端键入sql语句(本文例子select * from zzz)发送到服务端之后, my_net_read返回, 并从tcpbuffer中读取数据写入到packet这个字符串.
 
packet_length= my_net_read(net);
 
packet的第一个字节是个标志位, 决定数据包是查询还是命令,成功,或者出错。
 
接下来就进入dispatch_command(sql/sql/parse.cc)这个函数, 数据类型不再需要.
 
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
 
进入dispatch_command, 我们可见
 
statistic_increment(thd->status_var.questions, &LOCK_status);
 
这个就是show status questions的值累加.
 
接下的mysql_parse(sql/sql_parse.cc), 该函数是sql语句解析的总路口.
 
进入该函数后首先碰到的是query_cache_send_result_to_client,故名思义这个函数是在QCache里查询是否有相同的语句, 有则立即从QCache返回结果, 于是整个sql就结束了.
 
QCache里不存在的sql则继续前进来到parse_sql(sql/sql_parse.cc),这个函数主要就是调用了MYSQLparse. 而MYSQLparse其实就是bison/yacc里的yyparse(^_^),
 
#define yyparse MYSQLparse
 
是的开始解析sql了. 关于词法分析和语法匹配简单说几下.
 
对于一条像select * from zzz的语句首先进入词法分析,找到2个token(select, from), 然后根据token进行语法匹配, 规则在sql/yacc.yy里, 我把几个匹配到的pattern和action贴出来.
 
select:
 
select_init
 
{
 
LEX *lex= Lex;
 
lex->sql_command= SQLCOM_SELECT;
 
}
 
;
 
/* Need select_init2 for subselects. */
 
select_init:
 
SELECT_SYM select_init2
 
| '(' select_paren ')' union_opt
 
;
 
select_paren:
 
SELECT_SYM select_part2
 
{
 
LEX *lex= Lex;
 
SELECT_LEX * sel= lex->current_select;
 
.....
 
select_from:
 
FROM join_table_list where_clause group_clause having_clause
 
opt_order_clause opt_limit_clause procedure_clause
 
{
 
Select->context.table_list=
 
Select->context.first_name_resolution_table=
 
(TABLE_LIST *) Select->table_list.first;
 
}
 
....
 
select_item_list:
 
select_item_list ',' select_item
 
| select_item
 
| '*'
 
{
 
THD *thd= YYTHD;
 
Item *item= new (thd->mem_root)
 
Item_field(&thd->lex->current_select->context,
 
NULL, NULL, "*");
 
if (item == NULL)
 
MYSQL_YYABORT;
 
if (add_item_to_list(thd, item))
 
MYSQL_YYABORT;
 
(thd->lex->current_select->with_wild)++;
 
}
 
;
 
select_item:
 
remember_name select_item2 remember_end select_alias
 
{
 
THD *thd= YYTHD;
 
DBUG_ASSERT($1 if (add_item_to_list(thd, $2))
 
MYSQL_YYABORT;
 
if ($4.str)
 
...
 
可以看到action里最关键的就是add_item_to_list 和table_list的赋值.
 
解析后对于需要查询的表(zzz)和字段(*)这些信息都写入到thd->lex这个结构体里了.
 
例如其中thd->lex->query_tables就是表(zzz)的状况, thd->lex->current_select->with_wild 是表示该语句是否使用了*等等.
 
(gdb) p *thd->lex->query_tables
 
$7 = {next_local = 0x0, next_global = 0x0, prev_global = 0x855a458, db = 0x85a16b8 "test", alias = 0x85a16e0 "zzz",
 
table_name = 0x85a16c0 "zzz", schema_table_name = 0x0, option = 0x0, on_expr = 0x0, prep_on_expr = 0x0, cond_equal = 0x0,
 
sql解析完了, 优化呢? 别急接着往下看.
 
接着进入mysql_execute_command函数,这个函数是所有sql命令的总入口.
 
由于是这个sql是一个select, 于是execute_sqlcom_select就是我们下个要执行的函数,又然后进入了mysql_select(^_^怒了如此复杂).
 
mysql_select 就是优化器的模块, 这个模块代码比较复杂. 我们可以清楚看到创建优化器,优化,执行的3个步骤, 优化细节不表.
 
if (!(join= new JOIN(thd, fields, select_options, result)))
 
...
 
if ((err= join->optimize()))
 
...
 
join->exec();
 
结束了优化,我们要具体执行join->exec(),该函数实际进入的是JOIN::exec()(sql_select.cc)。
 
exec()首先向客户端发送字段title的函数send_fields, 没数据但字段也是要的。
 
然后再进入do_select(),根据表的存储引擎跳入到引擎具体的实现(zzz是myisam表)。
 
这里mi_scan就是myisam引擎扫描文件的函数,再看到
 
(gdb) p info->filename
 
./test/zzz
 
这不就是zzz表对应的物理文件吗。
 
通过一系列的mi函数访问磁盘拿到数据之后,会通过send_data发送数据给client,并从dispatch_command返回.最后在net_end_statement结束整个sql。
 
一个简单的select语句背后的执行过程是非常复杂的,上面的步骤都只是点到就止。
 
ps: 在sql_yacc.yy可见MySQL对于Oracle中常用的dual表的嘲讽。
 
在不久的将来,云计算一定会彻底走入我们的生活,有兴趣入行未来前沿产业的朋友,可以收藏云计算,及时获取人工智能、大数据、云计算和物联网的前沿资讯和基础知识,让我们一起携手,引领人工智能的未来!

(编辑:宁德站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读