MySQL核心类-THD-生命周期

MySQL核心类-THD-生命周期

THD是MySQL server层最核心的类,没有之一,基本上所有的信息都是和THD 实例进行绑定的。对于每一个客户端链接进来后,系统会实例化一个THD对象,而后这个对象伴随这个链接的一生。

初始化过程

mysqld启动完毕后,main线程负责监听数据库对外服务端口,在循环中接受外部链接请求

代码如下

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);
 }
 }
1
2
3
4
5
6
7
8
9
10

从这里也可以看出,在短链接压力测试下,这会成为MySQL的一个瓶颈,本文中看到的是优化过后的5.7的代码,而之前的版本中,在初始化链接时,将更多的工作交给了main线程来完成,性能比较差。

listen_for_connection_event

Channel_info* Mysqld_socket_listener::listen_for_connection_event()
{
#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
1
2
3
4
5
6
7
8
9

在支持poll的操作系统中,使用poll,否则使用select机制,poll相对于select机制的优化点网上有一大堆。

随后进入 mgr->process_new_connection(channel_info)

Connection_handler_manager::process_new_connection(Channel_info* channel_info)
{
 if (abort_loop || !check_and_incr_conn_count())
 {
 channel_info->send_error_and_close_channel(ER_CON_COUNT_ERROR, 0, true);
 delete channel_info;
 return;
 }
 if (m_connection_handler->add_connection(channel_info))
 {
 inc_aborted_connects();
 delete channel_info;
 }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

MySQL支持很多种链接方式,包括企业版的连接池,都在这里体现。m_connection_handler->add_connection(channel_info)

而线上一般使用的是Per_thread_connection_handler

bool Per_thread_connection_handler::add_connection(Channel_info* channel_info)
{
 int error= 0;
 my_thread_handle id;
 DBUG_ENTER("Per_thread_connection_handler::add_connection");
 // Simulate thread creation for test case before we check thread cache
 DBUG_EXECUTE_IF("fail_thread_create", error= 1; goto handle_error;);
 if (!check_idle_thread_and_enqueue_connection(channel_info))
 DBUG_RETURN(false);
 /*
 There are no idle threads avaliable to take up the new
 connection. Create a new thread to handle the connection
 */
 channel_info->set_prior_thr_create_utime();
 error= mysql_thread_create(key_thread_one_connection, &id,
 &connection_attrib,
 handle_connection,
 (void*) channel_info);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

mysql_thread_create函数会创建一个新的线程

然后开始关注线程函数

pfs_spawn_thread是初始化线程的入口函数,下面来看他是如何生成THD对象的

pfs_spawn_thread会调用handle_connection,主要负责如下几部分的工作

  • Initialize thread
  • Initialize THD to be used with this thread
  • Authenticate user
  • Execute all queries sent on the connection
  • Take connection down
  • End thread / Handle next connection using thread from thread cache

初始化thd

THD *thd= init_new_thd(channel_info);
1

写到这里,忽然想到了一个问题,全局变量max_connections是在哪里生效的呢?这个问题另开一篇短文来介绍吧,先略过。

其他初始化操作,这里又涉及到线程重用的机制。

#ifdef HAVE_PSI_THREAD_INTERFACE
 if (pthread_reused)
 {
 /*
 Reusing existing pthread:
 Create new instrumentation for the new THD job,
 and attach it to this running pthread.
 */
 PSI_thread *psi= PSI_THREAD_CALL(new_thread)
 (key_thread_one_connection, thd, thd->thread_id());
 PSI_THREAD_CALL(set_thread_os_id)(psi);
 PSI_THREAD_CALL(set_thread)(psi);
 }
#endif
#ifdef HAVE_PSI_THREAD_INTERFACE
 /* Find the instrumented thread */
 PSI_thread *psi= PSI_THREAD_CALL(get_thread)();
 /* Save it within THD, so it can be inspected */
 thd->set_psi(psi);
#endif /* HAVE_PSI_THREAD_INTERFACE */
 mysql_thread_set_psi_id(thd->thread_id());
 mysql_thread_set_psi_THD(thd);
 mysql_socket_set_thread_owner(
 thd->get_protocol_classic()->get_vio()->mysql_socket);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

加入到线程队列中

thd_manager->add_thd(thd);
1

权限验证,从这里也可以看出,即时用户密码错误,MySQL也会分配线程,这是否占用线程资源呢?

thd_prepare_connection(thd)
1

如果以上都没有问题,则开始在一个循环中接收处理客户端命令了

while (thd_connection_alive(thd))
 {
 if (do_command(thd))
 break;
 }
1
2
3
4
5

THD销毁

如果链接超时,或者客户端主动close掉链接,会销毁掉thd对象,大概有如下几步。

  • end_connection(thd);

在end_connection操作中,会释放某个用户的链接,这在限制用户链接的功能中有用到。

  • close_connection(thd, 0, false, false);

真正的关闭链接,以及关闭前的处理操作

  • thd->release_resources();

释放线程资源

  • thd_manager->remove_thd(thd);

从线程队列中踢除

再往下就是线程重用了

  • channel_info= Per_thread_connection_handler::block_until_new_connection();

是否缓存此线程和MySQL中的全局变量thread_cache_size相关,如果当前缓存的线程数小于此值,则此线程会进入缓存状态,等待新的客户端到来,然后唤醒,否则的话,就退出线程了。

MySQL核心类-THD-生命周期

相关推荐