vlambda博客
学习文章列表

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方法。

MySQL代码分析|查询流程

handle_connection中会创建一个新的THD *thd对象。这个相当于session对象,上面放着很多和此次SQL请求处理相关信息。query,result等。然后就开始执行SQL语句。mysql_execute_command函数炒鸡长,主要是用一个switch判断了所有SQL命令类型。然后走不同处理逻辑。对于SQLCOM_SELECT来说,执行lex->m_sql_cmd->execute(thd)这个函数。

MySQL代码分析|查询流程

LEX *lexTHD *thd获取,包含SQL命令一些共同属性,以及执行状态等。lex中m_sql_cmd实例是某类型SQL语句执行器。这里是基类指针,具体select查询对应子类是Sql_cmd_dml

Sql_cmd_dml::execute_inner调用优化器做查询计划优化。然后开始执行。如果是explain语句,那就只做explain。

MySQL代码分析|查询流程

Query_block是代表查询块,而Query_expression是包含多个查询块的查询表达式。在ExecuteIteratorQuery中会处理各个Query_block。然后执行是通过m_root_iterator迭代器来迭代执行。这个迭代器又是基类指针,具体有多种迭代器实现。例如全表扫描,带索引的查询等还不一样。基本上都会实现InitRead方法。带索引查找实现在IndexScanIterator中。

看到最终走到ha_xxx_xxx方法中。这些handler方法由不同存储引擎实现。mysql支持不同类型存储引擎就是通过这种方式。不同存储引擎代码实现在storage/目录中。好了,今天先到这里,后续准备继续看下innoDB存储引擎代码。