MySQL 源码解读 -- 查询过程

Query 流程

转载:query_flow
query_flow
query_flow2
本文简单介绍, 详情参考query

query 入口

do_command函数在sql/sql_parse.cc定义,代码如下:

1
2
3
4
5
6
7
bool do_command(THD *thd) {
NET *net= &thd->net;
packet_length = my_net_read(net);
packet = (char*) net->read_pos;
command = (enum enum_server_command) (uchar) packet[0]; // 解析客户端传过来的命令类型
dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
}

dispatch

再看dispatch_command函数在sql/sql_parse.cc定义,精简代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
bool dispatch_command(enum enum_server_command command, THD thd, char packet, uint packet_length) {
NET *net = &thd->net;
thd->command = command;
switch (command) { //判断命令类型
case COM_INIT_DB: ...;
case COM_TABLE_DUMP: ...;
case COM_CHANGE_USER: ...;
...
case COM_QUERY: //如果是Query
alloc_query(thd, packet, packet_length); //从网络数据包中读取Query, 并扩容内存存入thd->set_query, shrink thd->packet/thd->convert_buffer,
mysql_parse(thd, thd->query, thd->query_length, &end_of_stmt); //送去解析
}
}

parser

mysql_parse函数负责解析SQL(sql/sql_parse.cc), 详情可以参考mysql_parser 精简代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void mysql_parse(THD *thd,
const
char *inBuf,
uint
length, const
char ** found_semicolon) {

lex_start(thd); //初始化线程解析描述符

if (query_cache_send_result_to_client(thd, (char*) inBuf,length) <= 0) {
// 看query cache中有否命中,有就直接返回结果,否则进行查找
Parser_state parser_state(thd, inBuf, length);
parse_sql(thd, & parser_state, NULL); // 解析SQL语句
mysql_execute_command(thd); // 执行

}

mysql_rewrite_query(thd);

error = mysql_execute_command(thd, true);
}

query 处理

终于开始执行mysql_execute_command接近3k行, 优化阶段和执行阶段揉在一起, 可以参考execute非常精简代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int mysql_execute_command(THD *thd) {
LEX *lex= thd->lex; // 解析过后的SQL语句的语法结构
TABLE_LIST *all_tables = lex->query_tables; // 该语句要访问的表的列表
switch (lex->sql_command) {
...
case SQLCOM_INSERT:
insert_precheck(thd, all_tables);
mysql_insert(thd, all_tables, lex->field_list, lex->many_values, lex->update_list, lex->value_list, lex->duplicates, lex->ignore);
break; ...
case SQLCOM_SELECT:
select_precheck(thd,lex, all_tables, first_table)); // 检查用户对数据表的访问权限
execute_sqlcom_select(thd, all_tables); // 执行select语句
break;
}
}
bool select_precheck(THD *thd, LEX *lex, TABLE_LIST *tables,
TABLE_LIST *first_table)
{
if (tables)
{
res= check_table_access(thd,
privileges_requested,
tables, FALSE, UINT_MAX, FALSE) ||
(first_table && first_table->schema_table_reformed &&
check_show_access(thd, first_table));
}
else
res= check_access(thd, privileges_requested, any_db, NULL, NULL, 0, 0);

return res;
}