MySQL连接的建立与使用
在 MYSQL的启动过程中,可以看到在 mysqld_main() 函数的最后调用了 mysqld_socket_acceptor->connection_event_loop() 函数用来处理MySQL的连接,这里通过源码分析一下MySQL连接的建立与使用过程:
1. 在死循环中调用 m_listener->listen_for_connection_event() 等待连接进入
2. 如果有连接进入,则调用 precess_new_connection() 处理连接
3. 调用 check_and_incr_conn_count() 检查是否有空余连接[当前连接数是否大于 max_connections], 如果没有空余连接, 结束处理流程;
注意:这里允许 max_connections + 1 个连接,最后一个连接是为 super user保留的。
4. 如果存在空余连接,则对连接进行处理
5. 查看 thread cache 中是否有空闲 thread,如果有,使用 cached thread
6. 如果不存在,则创建一个新的线程来处理这个连接
7. 线程调用 handle_connection() 线程处理函数,初始化一个 thd 对象,并将其加入 thd list; 并初始化 lex 词法解析器,进行连接身份验证,初始化 thd,准备执行语句。
8. 调用 do_command(). 处理用户命令。
/**
Connection acceptor loop to accept connections from clients.
*/
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);
}
}
// 如果有连接进入,处理连接
void Connection_handler_manager::process_new_connection(Channel_info* channel_info) {
// check_and_incr_conn_count() 检查是否有空余连接, 如果没有空余连接, 结束处理流程 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; } // 这里有空余连接, connection_accepted = true if (m_connection_handler->add_connection(channel_info)) { inc_aborted_connects(); delete channel_info; } }
// 在这里创建连接
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;); // 检查 thread cache 中是否有空闲 thread,如果有,使用 cached thread 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); #ifndef NDEBUG handle_error: #endif // !NDEBUG if (error) { connection_errors_internal++; if (!create_thd_err_log_throttle.log()) sql_print_error("Can't create thread to handle new connection(errno= %d)", error); channel_info->send_error_and_close_channel(ER_CANT_CREATE_THREAD, error, true); Connection_handler_manager::dec_connection_count(); DBUG_RETURN(true); } Global_THD_manager::get_instance()->inc_thread_created(); DBUG_PRINT("info", ("Thread created")); DBUG_RETURN(false); }
bool Per_thread_connection_handler::check_idle_thread_and_enqueue_connection(
Channel_info *channel_info)
{
bool res = true;
mysql_mutex_lock(&LOCK_thread_cache);
// 如果 blocked_pthread > wake_thread,则 thread cache 中存在空闲 thread
if (Per_thread_connection_handler::blocked_pthread_count > wake_pthread)
{
DBUG_PRINT("info", ("waiting_channel_info_list->push %p", channel_info));
// 将 channel_info 放入 waiting_channel_info_list,wake_pthread ++
waiting_channel_info_list->push_back(channel_info);
wake_pthread++;
mysql_cond_signal(&COND_thread_cache);
res = false;
}
mysql_mutex_unlock(&LOCK_thread_cache);
return res;
}
/**
Thread handler for a connection
线程处理函数。
@param arg Connection object (Channel_info)
This function (normally) does the following:
- 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
*/
extern "C" void *handle_connection(void *arg)
{
Global_THD_manager *thd_manager = Global_THD_manager::get_instance();
Connection_handler_manager *handler_manager =
Connection_handler_manager::get_instance();
Channel_info *channel_info = static_cast<Channel_info *>(arg);
bool pthread_reused MY_ATTRIBUTE((unused)) = false;
if (my_thread_init())
{
connection_errors_internal++;
channel_info->send_error_and_close_channel(ER_OUT_OF_RESOURCES, 0, false);
handler_manager->inc_aborted_connects();
Connection_handler_manager::dec_connection_count();
delete channel_info;
my_thread_exit(0);
return NULL;
}
for (;;)
{
// 初始化一个 thd 对象
THD *thd = init_new_thd(channel_info);
if (thd == NULL)
{
connection_errors_internal++;
handler_manager->inc_aborted_connects();
Connection_handler_manager::dec_connection_count();
break; // We are out of resources, no sense in continuing.
}
// 将新创建的 thd 添加到 thd list;
thd_manager->add_thd(thd);
/**
1. 初始化 lex 词法解析器
2. 进行连接身份验证
3. 初始化 thd , 准备执行语句
*/
if (thd_prepare_connection(thd))
handler_manager->inc_aborted_connects();
else
{
// 只要连接 alive,就会一直循环下去
while (thd_connection_alive(thd))
{
// 处理命令,这是 MySQL 的核心操作
if (do_command(thd))
break;
}
// 减少当前用户的连接计数等
end_connection(thd);
}
// 关闭一个连接
close_connection(thd, 0, false, false);
// 释放资源
thd->get_stmt_da()->reset_diagnostics_area();
thd->release_resources();
// Clean up errors now, before possibly waiting for a new connection.
#if OPENSSL_VERSION_NUMBER < 0x10100000L
ERR_remove_thread_state(0);
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
// 从 thd list 中移除 thd
thd_manager->remove_thd(thd);
// 减少当前连接数
Connection_handler_manager::dec_connection_count();
delete thd;
if (abort_loop) // Server is shutting down so end the pthread.
break;
// 阻塞当前的物理线程,使得物理线程被新的连接重用
channel_info = Per_thread_connection_handler::block_until_new_connection();
if (channel_info == NULL)
break;
pthread_reused = true;
if (abort_loop)
{
// Close the channel and exit as server is undergoing shutdown.
channel_info->send_error_and_close_channel(ER_SERVER_SHUTDOWN, 0, false);
delete channel_info;
channel_info = NULL;
Connection_handler_manager::dec_connection_count();
break;
}
}
my_thread_end();
my_thread_exit(0);
return NULL;
}
/**
Block the current pthread for reuse by new connections.
阻塞当前的物理线程,供新的连接使用
@retval NULL Too many pthreads blocked already or shutdown in progress.
@retval !NULL Pointer to Channel_info object representing the new connection
to be served by this pthread.
*/
Channel_info *Per_thread_connection_handler::block_until_new_connection()
{
Channel_info *new_conn = NULL;
mysql_mutex_lock(&LOCK_thread_cache);
if (blocked_pthread_count < max_blocked_pthreads &&
!kill_blocked_pthreads_flag)
{
/* Don't kill the pthread, just block it for reuse */
DBUG_PRINT("info", ("Blocking pthread for reuse"));
/*
mysys_var is bound to the physical thread,
so make sure mysys_var->dbug is reset to a clean state
before picking another session in the thread cache.
*/
DBUG_POP();
assert(!_db_is_pushed_());
// Block pthread ++
blocked_pthread_count++;
while (!abort_loop && !wake_pthread && !kill_blocked_pthreads_flag)
// 这里等待信号
mysql_cond_wait(&COND_thread_cache, &LOCK_thread_cache);
blocked_pthread_count--;
if (kill_blocked_pthreads_flag)
mysql_cond_signal(&COND_flush_thread_cache);
else if (wake_pthread)
{
// wake_pthread 计数器 -1
wake_pthread--;
if (!waiting_channel_info_list->empty())
{
// 如果 waiting_channel_info_list 不是空的, 则取出第一个
new_conn = waiting_channel_info_list->front();
waiting_channel_info_list->pop_front();
DBUG_PRINT("info", ("waiting_channel_info_list->pop %p", new_conn));
}
else
{
assert(0);
}
}
}
mysql_mutex_unlock(&LOCK_thread_cache);
return new_conn;
}
浙公网安备 33010602011771号