本文从一个select语句的执行过程出发, 遍历MySQL的多个几子系统.
先放图一张, 按图索骥开始咱们的历险.
当客户端链接上MySQL服务端以后,发出请求以前,服务端的线程是阻塞在do_command(sql/parse.cc)里的my_net_read函数中(就是socket里的read).
当客户端键入sql语句(本文例子select * from zzz)发送到服务端以后, my_net_read返回, 并从tcpbuffer中读取数据写入到packet这个字符串.
html
packet的第一个字节是个标志位, 决定数据包是查询仍是命令,成功,或者出错。
接下来就进入dispatch_command(sql/sql/parse.cc)这个函数,数据类型再也不须要.
mysql
进入dispatch_command, 咱们可见
sql
这个就是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(^_^),
socket
是的开始解析sql了. 关于词法分析和语法匹配简单说几下.
对于一条像select * from zzz的语句首先进入词法分析,找到2个token(select, from),
而后根据token进行语法匹配, 规则在sql/yacc.yy里,
我把几个匹配到的pattern和action贴出来.
tcp
能够看到action里最关键的就是add_item_to_list 和table_list的赋值.
解析后对于须要查询的表(zzz)和字段(*)这些信息都写入到thd->lex这个结构体里了.
例如其中thd->lex->query_tables就是表(zzz)的情况, thd->lex->current_select->with_wild 是表示该语句是否使用了*等等.
函数
sql解析完了, 优化呢? 别急接着往下看.
接着进入mysql_execute_command函数,这个函数是全部sql命令的总入口.
因为是这个sql是一个select, 因而execute_sqlcom_select就是咱们下个要执行的函数,
又而后进入了mysql_select(^_^怒了如此复杂).
mysql_select 就是优化器的模块, 这个模块代码比较复杂. 咱们能够清楚看到建立优化器,优化,执行的3个步骤, 优化细节不表.
优化
结束了优化,咱们要具体执行join->exec(), 该函数实际进入的是JOIN::exec()(sql_select.cc).
exec()首先向客户端发送字段title的函数send_fields, 没数据但字段也是要的.
而后再进入do_select(), 根据表的存储引擎跳入到引擎具体的实现(zzz是myisam).
这里mi_scan(info, buf) 就是myisam引擎扫描文件的函数,再看到info->filename=’./test/zzz’,不就是zzz表对应的物理文件吗.
经过一系列的mi函数访问磁盘拿到数据以后,会经过send_data发送数据给client,并从dispatch_command返回.最后在net_end_statement结束整个sql.
一个简单的select语句背后的执行过程是很是复杂的. 上面的步骤都只是点到就止.
ps: 在sql_yacc.yy可见MySQL对于Oracle中经常使用的dual表的嘲讽.ui