最近两天自己简单分析了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 源码