MySQL代码分析|查询流程
这两天准备读一下mysql-8.0代码。初步看了查询链路代码,简单总结下。
mysqld_main
是mysql服务端进程启动入口,执行到最后按照惯例进入事件循环。进入实现循环后就等待客户端发出请求,然后分发处理单个请求。整个流程如下。
mysqld_main // (sql/mysqld.cc)
-->mysqld_socket_acceptor->connection_event_loop() // (sql/conn_handler/connection_acceptor.h)
-->mgr->process_new_connection(channel_info) // (sql/conn_handler/connection_handler_manager.cc)
-->m_connection_handler->add_connection(channel_info) // (sql/conn_handler/connection_handler_per_thread.cc)
-->handle_connection
-->do_command(THD *thd) // (sql/sql_parse.cc)
-->dispatch_command
-->switch() case COM_QUERY:
-->alloc_query
-->copy_bind_parameter_values
-->dispatch_sql_command
-->parse_sql
-->mysql_execute_command
-->switch() case SQLCOM_SELECT:
-->lex->m_sql_cmd->execute(thd)
-->Sql_cmd_dml::execute(THD *thd) // (sql/sql_select.cc)
-->Sql_cmd_dml::execute_inner
-->Query_expression::execute(THD *thd) // (sql/sql_union.cc)
-->Query_expression::ExecuteIteratorQuery
-->m_root_iterator->Init() // (sql/iterators/*_iterators.cc)
-->IndexScanIterator<Reverse>::Init()/TableScanIterator::Init()/...
-->table()->file->ha_<index/rnd>_init
-->for(;;) m_root_iterator->Read()
-->IndexScanIterator<Reverse>::Read()/TableScanIterator::Read()/...
-->table()->file->ha_<index/rnd>_first(m_record);
-->table()->file->ha_<index/rnd>_next(m_record);
-->table()->file->ha_<index/rnd>_prev(m_record);
connection_event_loop
是一个循环,负责不断accept请求,然后分发处理。从Mysqld_socket_listener::listen_for_connection_event()
实现来看,目前还是用的poll/select
。
add_connection
有两种实现,分别是单线程模式和多线程模式。多线程模式下对每个请求分配一个空闲线程来处理。每个线程会调用handle_connection
方法。
在handle_connection
中会创建一个新的THD *thd
对象。这个相当于session对象,上面放着很多和此次SQL请求处理相关信息。query,result等。然后就开始执行SQL语句。mysql_execute_command
函数炒鸡长,主要是用一个switch判断了所有SQL命令类型。然后走不同处理逻辑。对于SQLCOM_SELECT
来说,执行lex->m_sql_cmd->execute(thd)
这个函数。
LEX *lex
从THD *thd
获取,包含SQL命令一些共同属性,以及执行状态等。lex中m_sql_cmd
实例是某类型SQL语句执行器。这里是基类指针,具体select查询对应子类是Sql_cmd_dml
。
Sql_cmd_dml::execute_inner
调用优化器做查询计划优化。然后开始执行。如果是explain语句,那就只做explain。
Query_block
是代表查询块,而Query_expression
是包含多个查询块的查询表达式。在ExecuteIteratorQuery
中会处理各个Query_block
。然后执行是通过m_root_iterator
迭代器来迭代执行。这个迭代器又是基类指针,具体有多种迭代器实现。例如全表扫描,带索引的查询等还不一样。基本上都会实现Init
和Read
方法。带索引查找实现在IndexScanIterator
中。
看到最终走到ha_xxx_xxx
方法中。这些handler方法由不同存储引擎实现。mysql支持不同类型存储引擎就是通过这种方式。不同存储引擎代码实现在storage/
目录中。好了,今天先到这里,后续准备继续看下innoDB存储引擎代码。