1 先来张图
查询的流程(图片来自《MySQL 是怎样运行的:从根儿上理解 MySQL》)。本文我们主要涉及解析与优化,以及执行的过程。

2 do_command 流程简析
如上文 MySQL启动流程与连接简析 所述,指令的处理在do_command 中。 那么do_command 中做了什么呢?本文简单讲解一下。
bool do_command(THD *thd)
{
...
if (classic)
{
/*
This thread will do a blocking read from the client which
will be interrupted when the next command is received from
the client, the connection is closed or "net_wait_timeout"
number of seconds has passed.
*/
// 如英文注释所示,线程将会从客户端中监听指令。
net= thd->get_protocol_classic()->get_net();
my_net_set_read_timeout(net, thd->variables.net_wait_timeout);
net_new_transaction(net);
}
...
// 从网络中读取
if (classic)
my_net_set_read_timeout(net, thd->variables.net_read_timeout);
// dispatch_command 是重点,下面再分析。
return_value= dispatch_command(thd, &com_data, command);
thd->get_protocol_classic()->get_packet()->shrink(
thd->variables.net_buffer_length);
...
}
如代码段所示,do_command 的主要用途是读取网络连接中client传递过来的指令,然后解析,最后交给dispatch_command 处理。 下面我们再分析一下dispatch_command 的主要流程。
3 dispatch_command 流程分析
bool dispatch_command(THD *thd, const COM_DATA *com_data,
enum enum_server_command command)
{
...
thd->set_command(command);
...
// 使用 lex 进行sql command 解析。
thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */
...
thd->set_query_id(next_query_id());
thd->rewritten_query.mem_free();
thd_manager->inc_thread_running();
...
// 权限校验
if (unlikely(thd->security_context()->password_expired() &&
command != COM_QUERY &&
command != COM_STMT_CLOSE &&
command != COM_STMT_SEND_LONG_DATA &&
command != COM_PING &&
command != COM_QUIT))
{
my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
goto done;
}
#ifndef EMBEDDED_LIBRARY
if (mysql_audit_notify(thd,
AUDIT_EVENT(MYSQL_AUDIT_COMMAND_START),
command, command_name[command].str))
{
goto done;
}
#endif /* !EMBEDDED_LIBRARY */
// 下面进入重点,分别处理各种指令。
switch (command) {
...
case COM_QUERY:
{
// 查询相关。
DBUG_ASSERT(thd->m_digest == NULL);
thd->m_digest= & thd->m_digest_state;
thd->m_digest->reset(thd->m_token_array, max_digest_length);
if (alloc_query(thd, com_data->com_query.query,
com_data->com_query.length))
break; // fatal error is set
MYSQL_QUERY_START(const_cast<char*>(thd->query().str), thd->thread_id(),
(char *) (thd->db().str ? thd->db().str : ""),
(char *) thd->security_context()->priv_user().str,
(char *) thd->security_context()->host_or_ip().str);
const char *packet_end= thd->query().str + thd->query().length;
if (opt_general_log_raw)
query_logger.general_log_write(thd, command, thd->query().str,
thd->query().length);
DBUG_PRINT("query",("%-.4096s", thd->query().str));
#if defined(ENABLED_PROFILING)
thd->profiling.set_query_source(thd->query().str, thd->query().length);
#endif
Parser_state parser_state;
if (parser_state.init(thd, thd->query().str, thd->query().length))
break;
mysql_parse(thd, &parser_state);
while (!thd->killed && (parser_state.m_lip.found_semicolon != NULL) &&
! thd->is_error())
{
/*
Multiple queries exits, execute them individually
*/
const char *beginning_of_next_stmt= parser_state.m_lip.found_semicolon;
/* Finalize server status flags after executing a statement. */
thd->update_server_status();
thd->send_statement_status();
query_cache.end_of_result(thd);
#ifndef EMBEDDED_LIBRARY
mysql_audit_notify(thd, AUDIT_EVENT(MYSQL_AUDIT_GENERAL_STATUS),
thd->get_stmt_da()->is_error() ?
thd->get_stmt_da()->mysql_errno() : 0,
command_name[command].str,
command_name[command].length);
#endif
size_t length= static_cast<size_t>(packet_end - beginning_of_next_stmt);
log_slow_statement(thd);
/* Remove garbage at start of query */
while (length > 0 && my_isspace(thd->charset(), *beginning_of_next_stmt))
{
beginning_of_next_stmt++;
length--;
}
/* PSI end */
MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
thd->m_statement_psi= NULL;
thd->m_digest= NULL;
/* DTRACE end */
if (MYSQL_QUERY_DONE_ENABLED())
{
MYSQL_QUERY_DONE(thd->is_error());
}
/* SHOW PROFILE end */
#if defined(ENABLED_PROFILING)
thd->profiling.finish_current_query();
#endif
/* SHOW PROFILE begin */
#if defined(ENABLED_PROFILING)
thd->profiling.start_new_query("continuing");
thd->profiling.set_query_source(beginning_of_next_stmt, length);
#endif
/* DTRACE begin */
MYSQL_QUERY_START(const_cast<char*>(beginning_of_next_stmt),
thd->thread_id(),
(char *) (thd->db().str ? thd->db().str : ""),
(char *) thd->security_context()->priv_user().str,
(char *) thd->security_context()->host_or_ip().str);
/* PSI begin */
thd->m_digest= & thd->m_digest_state;
thd->m_digest->reset(thd->m_token_array, max_digest_length);
thd->m_statement_psi= MYSQL_START_STATEMENT(&thd->m_statement_state,
com_statement_info[command].m_key,
thd->db().str, thd->db().length,
thd->charset(), NULL);
THD_STAGE_INFO(thd, stage_starting);
thd->set_query(beginning_of_next_stmt, length);
thd->set_query_id(next_query_id());
/*
Count each statement from the client.
*/
thd->status_var.questions++;
thd->set_time(); /* Reset the query start time. */
parser_state.reset(beginning_of_next_stmt, length);
/* TODO: set thd->lex->sql_command to SQLCOM_END here */
mysql_parse(thd, &parser_state);
}
/* Need to set error to true for graceful shutdown */
if((thd->lex->sql_command == SQLCOM_SHUTDOWN) && (thd->get_stmt_da()->is_ok()))
error= TRUE;
DBUG_PRINT("info",("query ready"));
break;
}
...
// 下面是一条指令结束之后的操作。
done:
...
/* Finalize server status flags after executing a command. */
thd->update_server_status();
if (thd->killed)
thd->send_kill_message();
thd->send_statement_status();
thd->rpl_thd_ctx.session_gtids_ctx().notify_after_response_packet(thd);
query_cache.end_of_result(thd);
log_slow_statement(thd);
THD_STAGE_INFO(thd, stage_cleaning_up);
thd->reset_query();
thd->set_command(COM_SLEEP);
thd->proc_info= 0;
thd->lex->sql_command= SQLCOM_END;
/* Performance Schema Interface instrumentation, end */
MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
thd->m_statement_psi= NULL;
thd->m_digest= NULL;
/* Prevent rewritten query from getting "stuck" in SHOW PROCESSLIST. */
thd->rewritten_query.mem_free();
thd_manager->dec_thread_running();
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
...
}
代码很长,其实核心就是:
- 解析sql
- 权限校验
- 执行sql
- 执行后善后操作。
代码中,switch case 中有好多类型的指令。指令的枚举类:

4 总结
本文简单的分析了指令处理的过程,下一篇文章中,我们将看下COM_QUERY这个指令的流程,顺便看下其中涉及编码的部分。
5 参考文献
写完文章,发现这篇文章讲的很体系,也放这儿供以后查阅。 www.cnblogs.com/end/archive…