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

      

  • 相关阅读:
    个人开发框架总结(六)
    使用Forms身份验证
    接口IStateManager的使用心得
    Remoting中向服务器传送新对象时应注意的问题
    具有反色文本的进度条绘制方法
    中文姓名的VbScript验证方法
    个人开发框架总结(七)
    Asp.net MVC 实例
    直接对List对象排序,提高系统性能
    Spring 简单实现邮件发送
  • 原文地址:https://www.cnblogs.com/juanmaofeifei/p/16146201.html
Copyright © 2020-2023  润新知