MySQL系列(5):mysqld之网络IO模型

引言

前面几节介绍了mysqld的初始化,接下来介绍连接的监听和处理。重点掌握TCP、Unix域套接字和poll模型,进一步可自行了解epoll模型。本文重在代码脉络的梳理和知识点的提取,相关技术细节可自行加餐。

知识点

select/poll模型;TCP;Unix域套接字

源码分析

网络IO模型

从Mysqld_socket_listener::listen_for_connection_event()可以看出,MySQL网络IO采用了select/poll模型,如下:

#ifdef HAVE_POLL
  int retval= poll(&m_poll_info.m_fds[0], m_socket_map.size(), -1);
#else
  m_select_info.m_read_fds= m_select_info.m_client_fds;
  int retval= select((int) m_select_info.m_max_used_connection,
                     &m_select_info.m_read_fds, 0, 0, 0);
#endif

poll模型主要解决了select的最大文件描述符限制,但不具备移植性,在Linux上有实现,Windows没有。MySQL在Linux上默认用的是poll,而Windows上则是select。

初始化网络

if (network_init())
    unireg_abort(MYSQLD_ABORT_EXIT);

network_init()里重点关注下面语句:

mysqld_socket_acceptor->init_connection_acceptor()

这里初始化连接器,进一步跟踪:

TCP_socket tcp_socket(m_bind_addr_str, m_tcp_port,
                          m_backlog, m_port_timeout);
Unix_socket unix_socket(&m_unix_sockname, m_backlog);

可以看出,MySQL监听了Unix Socket和TCP连接,分别用于处理本机和远程客户端的连接。

连接处理

在mysqld.cc主函数中,有个循环负责监听客户端连接,并创建线程处理。

mysqld_socket_acceptor->connection_event_loop();

connection_event_loop()核心部分如下:

while (!abort_loop)
{
  Channel_info *channel_info= m_listener->listen_for_connection_event();
  if (channel_info != NULL)
    mgr->process_new_connection(channel_info);
}

监听连接

listen_for_connection_event()监听连接事件,用poll监听客户端连接,有客户端请求时,用accept创建新socket。

int retval= poll(&m_poll_info.m_fds[0], m_socket_map.size(), -1);
connect_sock= mysql_socket_accept(key_socket_client_connection, listen_sock,
                                      (struct sockaddr *)(&cAddr), &length);

处理连接

process_new_connection里核心代码如下:

m_connection_handler->add_connection(channel_info)

继续跟踪进去,实际工作的是:

error= mysql_thread_create(key_thread_one_connection, &id,
                             &connection_attrib,
                             handle_connection,
                             (void*) channel_info);

其中handle_connection参数为线程处理函数,主要工作为初始化线程、构造THD对象、认证用户、循环处理命令、销毁连接。该函数里重点是循环处理命令:

while (thd_connection_alive(thd))
{
  if (do_command(thd))
    break;
}

do_command里需要分发命令,如下:

return_value= dispatch_command(thd, &com_data, command);

其中command参数是枚举类型,其取值范围如下:

enum enum_server_command
{
  COM_SLEEP,
  COM_QUIT,
  COM_INIT_DB,
  COM_QUERY,
  COM_FIELD_LIST,
  COM_CREATE_DB,
  COM_DROP_DB,
  COM_REFRESH,
  COM_SHUTDOWN,
  COM_STATISTICS,
  COM_PROCESS_INFO,
  COM_CONNECT,
  COM_PROCESS_KILL,
  COM_DEBUG,
  COM_PING,
  COM_TIME,
  COM_DELAYED_INSERT,
  COM_CHANGE_USER,
  COM_BINLOG_DUMP,
  COM_TABLE_DUMP,
  COM_CONNECT_OUT,
  COM_REGISTER_SLAVE,
  COM_STMT_PREPARE,
  COM_STMT_EXECUTE,
  COM_STMT_SEND_LONG_DATA,
  COM_STMT_CLOSE,
  COM_STMT_RESET,
  COM_SET_OPTION,
  COM_STMT_FETCH,
  COM_DAEMON,
  COM_BINLOG_DUMP_GTID,
  COM_RESET_CONNECTION,
  COM_END
};

如果执行的是SELECT语句,则command为COM_QUERY。switch语句里走对应分支:

mysql_parse(thd, &parser_state);

到这里就将进入解析器模块,下一节再介绍。


欢迎关注公众号,获取推送更方便,遇到问题来交流!

技术长跑

你可能感兴趣的:(MySQL)