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

  

posted @ 2022-04-14 19:31  卷毛狒狒  阅读(648)  评论(0编辑  收藏  举报