MYSQL SELECT 过程 转
2015-12-22 14:01
716 查看
本文从一个select语句的执行过程出发, 遍历MySQL的多个几子系统. 先放图一张, 按图索骥开始我们的历险. <ignore_js_op> 当客户端连接上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 < $3); 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(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表的嘲讽. |
相关文章推荐
- 使用mysql库连接mysql
- ucenter info:can not connect to MySQL server解决办法
- MySQL5.7更改密码时出现ERROR 1054 (42S22): Unknown column 'password' in 'field list'
- 解决MySQL显示中文的问题
- Async.js解决Node.js操作MySQL的回调大坑
- 编译安装MySQL
- MySQLDump在使用之前一定要想到的事情 [转载]
- MySQL错误:2003-Can't connect to MySQL server on 'localhost' (10061)
- mysql修改字符编码
- mysql自定义循环函数
- mysql数据表中文乱码解决方案
- MySQL数据库加密与解密
- 在Window上安装Mysql
- Mysql数据库int(1)和tinyint(1)的区别&&扩展阅读
- Mysql经常使用命令
- mysql 分组取最新的一条记录(整条记录)
- 转-MySQL - Show Processlist 整理
- 配置mysql默认编码格式 && 解决Can't connect to local MySQL server through socket 问题
- 树莓派上的mysql数据库中文乱码问题
- mysql数据库批量操作