MySQL启动流程与连接简析

2,002 阅读3分钟

最近两天自己简单分析了MySQL的启动过程,以及连接建立等过程,参考了几本经典的资料,整理下,也算是个小结。

MySQL 源码是C++的,所以入口是main() 函数。 main 函数中只有一个mysqld_main 函数。

1 各种系统变量的初始化

进入mysqld_main ,首先看到了好多变量的初始化。好多目前我还不知道具体是干嘛用的,就只展示几个核心的。如果感兴趣,可以看下《MySQL运维内参》。

if (load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv))
  {
    flush_error_log_messages();
    return 1;
  }

这个是加载配置。在我们启动mysqld 时指定了default-file 就用了这个函数。

  system_charset_info= &my_charset_utf8_general_ci;

这个配置了系统默认的编码格式,因为系统启动时传入了好多参数,需要解析。 在初始化部分,有一个是核心模块的初始化,单独拿出来说下。

2 核心模块的初始化

  if (init_server_components())
    unireg_abort(MYSQLD_ABORT_EXIT);

这个是初始化核心组件。里面有query cache 初始化,随机数初始化, transaction cache 初始化, bin log 相关的配置,慢查询日志相关,还有最重要的是存储引擎的设定。 后面其他初始化就不写了,暂时也不知道什么用途,用到了再回头看。

3 启动socket连接

MySQL也是C/S结构,所以服务端需要启动起来,等待客户端的连接,那我们就简单看下。

tcp/ip 模式,bind /listen/ accept 等待客户端请求的到来。代码如下:

mysqld_socket_acceptor->connection_event_loop();


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

上述函数的作用是: Connection acceptor loop to accept connections from clients. 其核心函数是 connection_event_loop 是个while循环,利用 listen_for_connection_event 进行poll或者select监听,如果发现有请求,就调用process_new_connection 产生一个新的连接。

下面我们简单看下上述提到的几个函数的主要用途: listen_for_connection_event,有如下三种方式:

我们来看下socket的方式:

Channel_info* Mysqld_socket_listener::listen_for_connection_event()
{
  // 通过poll监听
  int retval= poll(&m_poll_info.m_fds[0], m_socket_map.size(), -1);
  ...


  MYSQL_SOCKET connect_sock;
  struct sockaddr_storage cAddr;
  // 通过mysql_socket_accept 建立socket连接。
  for (uint retry= 0; retry < MAX_ACCEPT_RETRY; retry++)
  {
    socket_len_t length= sizeof(struct sockaddr_storage);
    connect_sock= mysql_socket_accept(key_socket_client_connection, listen_sock,
                                      (struct sockaddr *)(&cAddr), &length);
    if (mysql_socket_getfd(connect_sock) != INVALID_SOCKET ||
        (socket_errno != SOCKET_EINTR && socket_errno != SOCKET_EAGAIN))
      break;
  }
  ...
...

通过上述代码,可以看到,利用了poll模型,进行监听,然后建立连接。 下面看下 process_new_connection:

Connection_handler_manager::process_new_connection(Channel_info* channel_info)
{
  ...
  if (m_connection_handler->add_connection(channel_info))
  {
    inc_aborted_connects();
    delete channel_info;
  }
}

核心是add_connection 。 有几个实现,我们就看下One_thread_connection_handler::add_connection 吧。

bool One_thread_connection_handler::add_connection(Channel_info* channel_info)
{
  // 线程初始化, 为线程设置堆栈
  if (my_thread_init())
  {
    ...
  }

  THD* thd= channel_info->create_thd();

  thd->set_new_thread_id();

  thd->start_utime= thd->thr_create_utime= my_micro_time();

  thd_set_thread_stack(thd, (char*) &thd);
  ...

  bool error= false;
  if (thd_prepare_connection(thd))
   ...
  else
  {
    delete channel_info;
    // 这儿是重点,如果连接一直活着,则执行while循环,进行do_command 处理。
    while (thd_connection_alive(thd))
    {
      if (do_command(thd))
        break;
    }
    end_connection(thd);
  }
  // 如果连接死了,则回收资源。
  close_connection(thd, 0, false, false);
  thd->release_resources();
  thd_manager->remove_thd(thd);
  Connection_handler_manager::dec_connection_count();
  delete thd;
  return error;
}


负责执行的是sql_parse.cc的 do_command, 负责从连接中读取指令(比如query活着其他)并执行。 do_command 就暂时不分析了,后面到具体的使用时,我们再分析。

4 服务退出

如果最后服务停了,最后会是clean_up 跟 mysqld_exit 退出操作。

 clean_up(1);
  mysqld_exit(MYSQLD_SUCCESS_EXIT);

5 总结

本文从MySQL初始化,到服务端启动,连接建立,以及如何为连接设定处理方法等环节,简单地分析了一下,整体流程算是明白了一点,算作是流水账吧。毕竟自己对MySQL的了解还太浅,等分析一段时间之后,再回头来看这篇文章,加油~

6 参考文献

《MySQL运维内参》

《MySQL 是怎样运行的:从根儿上理解 MySQL》

MySQL 5.7 源码