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

从这里也可以看出,在短链接压力测试下,这会成为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);#endif123456789

在支持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; }}12345678910111213141516

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

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

然后开始关注线程函数

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

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

Initialize threadInitialize THD to be used with this threadAuthenticate userExecute all queries sent on the connectionTake connection downEnd 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);1234567891011121314151617181920212223242526

加入到线程队列中

thd_manager->add_thd(thd);1

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

thd_prepare_connection(thd)1

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

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

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相关,如果当前缓存的线程数小于此值,则此线程会进入缓存状态,等待新的客户端到来,然后唤醒,否则的话,就退出线程了。

查看原文 >>
相关文章