• MYSQL SELECT 过程 转


     
    本文从一个select语句的执行过程出发, 遍历MySQL的多个几子系统.

    先放图一张, 按图索骥开始我们的历险.

    <ignore_js_op>mysql_sql_adventure.jpg 
     



    当客户端连接上MySQL服务端之后,发出请求之前,服务端的线程是阻塞在do_command(sql/parse.cc)里的my_net_read函数中(就是socket里的read).

    当客户端键入sql语句(本文例子select * from zzz)发送到服务端之后, my_net_read返回, 并从tcpbuffer中读取数据写入到packet这个字符串.
    1. packet_length= my_net_read(net);
    复制代码
    packet的第一个字节是个标志位, 决定数据包是查询还是命令,成功,或者出错。

    接下来就进入dispatch_command(sql/sql/parse.cc)这个函数,数据类型不再需要.
    1. return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    复制代码
    进入dispatch_command, 我们可见
    1. 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(^_^),
    1. #define yyparse         MYSQLparse
    复制代码


    是的开始解析sql了. 关于词法分析和语法匹配简单说几下.

    对于一条像select * from zzz的语句首先进入词法分析,找到2个token(select, from), 
    然后根据token进行语法匹配, 规则在sql/yacc.yy里, 

    我把几个匹配到的pattern和action贴出来.
    1. select:
    2.           select_init
    3.           {
    4.             LEX *lex= Lex;
    5.             lex->sql_command= SQLCOM_SELECT;
    6.           }
    7.         ;
    8. /* Need select_init2 for subselects. */
    9. select_init:
    10.           SELECT_SYM select_init2
    11.         | '(' select_paren ')' union_opt
    12.         ;
    13. select_paren:
    14.           SELECT_SYM select_part2
    15.           {
    16.             LEX *lex= Lex;
    17.             SELECT_LEX * sel= lex->current_select;
    18. .....
    19. select_from:
    20.           FROM join_table_list where_clause group_clause having_clause
    21.           opt_order_clause opt_limit_clause procedure_clause
    22.           {
    23.             Select->context.table_list=
    24.               Select->context.first_name_resolution_table=
    25.                 (TABLE_LIST *) Select->table_list.first;
    26.           }
    27. ....
    28. select_item_list:
    29.           select_item_list ',' select_item
    30.         | select_item
    31.         | '*'
    32.           {
    33.             THD *thd= YYTHD;
    34.             Item *item= new (thd->mem_root)
    35.                           Item_field(&thd->lex->current_select->context,
    36.                                      NULL, NULL, "*");
    37.             if (item == NULL)
    38.               MYSQL_YYABORT;
    39.             if (add_item_to_list(thd, item))
    40.               MYSQL_YYABORT;
    41.             (thd->lex->current_select->with_wild)++;
    42.           }
    43.         ;    
    44. select_item:
    45.           remember_name select_item2 remember_end select_alias
    46.           {
    47.             THD *thd= YYTHD;
    48.             DBUG_ASSERT($1 < $3);
    49.             if (add_item_to_list(thd, $2))
    50.               MYSQL_YYABORT;
    51.             if ($4.str)
    52. ...
    复制代码


    可以看到action里最关键的就是add_item_to_list 和table_list的赋值.
    解析后对于需要查询的表(zzz)和字段(*)这些信息都写入到thd->lex这个结构体里了.

    例如其中thd->lex->query_tables就是表(zzz)的状况, thd->lex->current_select->with_wild 是表示该语句是否使用了*等等.
    1. (gdb) p *thd->lex->query_tables
    2. $7 = {next_local = 0x0, next_global = 0x0, prev_global = 0x855a458, db = 0x85a16b8 "test", alias = 0x85a16e0 "zzz",
    3.   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个步骤, 优化细节不表.
    1. if (!(join= new JOIN(thd, fields, select_options, result)))
    2. ...
    3. if ((err= join->optimize()))
    4. ...
    5. 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表的嘲讽.



  • 相关阅读:
    phpMyAdmin <= 4.0.4.1 import.php GLOBALS变量注入漏洞
    第一个VC++ win32程序 绘制简单图形
    QTP 9.2 下载&破解
    MacOS下使用VMware5 破解 安装win7 ISO 激活
    win7 原版下载&激活
    jQuery.ui autoComplete使用
    SubLime2 win + mac keygen
    为Chrome添加https搜索 自定义地址栏搜索引擎
    jQuery validate入门
    Bootstrap dropdown 使用
  • 原文地址:https://www.cnblogs.com/zengkefu/p/5066455.html
Copyright © 2020-2023  润新知