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);

到這裏就將進入解析器模塊,下一節再介紹。


歡迎關注公衆號,獲取推送更方便,遇到問題來交流!

技術長跑

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章