MySQL code解析

本文深入剖析MySQL的query处理流程,从入口函数mysql_execute_command出发,详细讲解了prepare statement的mysql_sql_stmt_prepare和mysql_sql_stmt_execute过程。重点分析了query parsing、query caching以及query的optimizer部分,包括parse_sql、mysql_select和JOIN的执行流程。文章揭示了MySQL如何从SQL语句到执行计划的转化,为理解MySQL内部工作原理提供了清晰的指导。
摘要由CSDN通过智能技术生成
# Query入口
MySQL的代码比较messy,为了清楚理解MySQL的已有功能,本篇文章从MySQL的query层面进行分析,目的是理清MySQL的query处理流程和代码架构。本文基于MySQL5.7版本。


MySQL的入口代码的skeloton可以参见官方文档:https://dev.mysql.com/doc/internals/en/guided-tour-skeleton.html
本文着重讲解query parse和optimize的部分。个人认为官方文档对入口代码的说明言简意赅,本文也将采用类似的形式进行代码拆解和分析。


我们首先延续官方文档中给出的入口函数(位于sql_parse.cc下):   
```
  void mysql_execute_command(THD *thd)
       switch (lex->sql_command) {
        case SQLCOM_SELECT: ...
        case SQLCOM_SHOW_ERRORS: ...
        case SQLCOM_CREATE_TABLE: ...
        case SQLCOM_UPDATE: ...
        case SQLCOM_INSERT: ...                   // !
        case SQLCOM_DELETE: ...
        case SQLCOM_DROP_TABLE: ...
  case SQLCOM_PREPARE:
    {
    mysql_sql_stmt_prepare(thd);
    break;
    }
    case SQLCOM_EXECUTE:
    {
    mysql_sql_stmt_execute(thd);
    break;
    }
       }
```
注意,在MySQL5.7中mysql_execute_command是由mysql_parse调用的。官方文档中调用函数为另一个(mysql_stmt_execute),但他们同为dispatch_command下调用的,所以官方文档仍然可以借鉴。


# Prepare Statement
上述入口代码上我们可以看到两处和Prepare Statement相关的地方mysql_sql_stmt_prepare和mysql_sql_stmt_execute。


## mysql_sql_stmt_prepare


我们先来看看mysql_sql_stmt_prepare做了什么事。如下所示,mysql_sql_stmt_prepare主要做了?件事。


```
stmt->set_name(name);
thd->stmt_map.insert(thd, stmt);
stmt->m_prepared_stmt= MYSQL_CREATE_PS(stmt, stmt->id,
                                       thd->m_statement_psi,
                                       stmt->name().str, stmt->name().length,
                                       query, query_len);
stmt->prepare(query, query_len);
```
这里的MYSQL_CREATE_PS对应了函数inline_mysql_create_prepared_stmt(IDENTITY, ID, LOCKER, NAME, NAME_LENGTH, SQLTEXT, SQLTEXT_LENGTH),其位于include/mysql/psi/mysql_ps.h:


```
inline_mysql_create_prepared_stmt(void *identity, uint stmt_id,
                                  PSI_statement_locker *locker,
                                  const char *stmt_name, size_t stmt_name_length,
                                  const char *sqltext, size_t sqltext_length)
{
  if (locker == NULL)
    return NULL;
  return PSI_PS_CALL(create_prepared_stmt)(identity, stmt_id, 
                                           locker,
                                           stmt_name, stmt_name_length,
                                           sqltext, sqltext_length);
}
```
我们可以看到,PSI_PS_CALL对应的执行的函数是create_prepared_stmt,该函数仅仅分配了prepare statement所需的各个数据结构。该函数其位于storage/perfschema/pfs_prepared_stmt.cc。


真正**对query进行parse的是stmt->prepare(query, query_len)**。这个stmt是其声明时定义的Prepared_statement。Prepared_statement这个类中有一个LEX成员变量lex,其保存了parse tree。注意:凡是看到LEX结构,就要想到其对应了parse tree。在源码中也指明了它是parse tree descriptor。prepare statement最重要的调用是parse_sql(thd, &parser_state, NULL)来解析sql语句生成parse tree。parse_sql调用的是sql_yacc.cc中的MYSQLparse来做的sql解析。


**注意,最后的结果都是在THD中。任何的数据结构想要访问query解析的结果,其都应该包含THD,通过THD来访问。**


## mysql_sql_stmt_execute


```
void mysqld_stmt_execute(THD *thd, ulong stmt_id, ulong flags, uchar *params,
                         ulong params_length)
{
Prepared_statement *stmt;
  stmt->execute_loop(&expanded_query, open_cursor, params,
                    params + params_length);
}
```
这里列出了最重要的内容,即执行函数execute_loop。代码中对exectue_loop的解释如下:


```
/**
  Execute a prepared statement. Re-prepare it a limited number
  of times if necessary.


  Try to execute a prepared statement. If there is a metadata
  validation error, prepare a new copy of the prepared statement,
  swap the old and the new statements, and try again.
  If there is a validation error again, repeat the above, but
  perform no more than MAX_REPREPARE_ATTEMPTS.


  @note We have to try several times in a loop since we
  release metadata locks on tables after prepared statement
  prepare. Therefore, a DDL statement may sneak in between prepare
  and execute of a new statement. If this happens repeatedly
  more than MAX_REPREPARE_ATTEMPTS times, we give up.


  @return TRUE if an error, FALSE if success
  @retval  TRUE    either MAX_REPREPARE_ATTEMPTS has been reached,
                   or some general error
  @retval  FALSE   successfully executed the statement, perhaps
                   after having reprepared it a few times.
*/
Prepared_statement::execute_loop(String *expanded_query,
                                 bool open_cursor,
                                 uchar *packet,
                                 uchar *packet_end)
{
...
reexecute:
error= execute(expanded_query, open_cursor) || thd->is_error();
    if (error) {
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值