程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> MySQL綜合教程 >> Mysql源碼學習——Connection Manager

Mysql源碼學習——Connection Manager

編輯:MySQL綜合教程

 

1.連接的線程數

 

       Mysql支持單線程和多線程兩種連接線程數。如果是單線程,則在同一時刻,只能有一個connection連接到Mysql,

 

其他的連接會被掛起。如果是多線程,則同一時刻可以支持多個connection同時連接到服務器。

 

     可以通過設置服務器的啟動參數來設定連接的線程數:

 

mysqld.exe --thread-handling=no-threads

mysqld.exe --thread-handling=one-thread-per-connection

 

  

 

服務器如何通過參數來選擇使用哪種方式的呢?且看服務器中的分支代碼:

 

#ifdef EMBEDDED_LIBRARY

  one_thread_scheduler(&thread_scheduler);

#else

  if (global_system_variables.thread_handling <=

      SCHEDULER_ONE_THREAD_PER_CONNECTION)

    one_thread_per_connection_scheduler(&thread_scheduler);

  else if (global_system_variables.thread_handling == SCHEDULER_NO_THREADS)

    one_thread_scheduler(&thread_scheduler);

  else

    pool_of_threads_scheduler(&thread_scheduler);  /* purecov: tested */

#endif

 

  

 

這段代碼出現在get_options函數中,此函數是根據傳入的服務器參數,設置相應參數的模式。由這段代碼可以看出,如果定義了EMBEDDED_LIBRARY宏定義(估計應該是嵌入式使用),則調用one_thread_scheduler,即使用單線程。如果參數小於等於SCHEDULER_ONE_THREAD_PER_CONNECTION,則調用one_thread_per_connection_scheduler,即每個連接一個線程,即多線程。 至於global_system_variables.thread_handling是如何進行設置的呢?其實就是根據我們傳遞給服務器的參數--thread-handling進行設置的,參數的設置統一在函數get_options中,其調用mysqld_get_one_option,其中有個分支,代碼如下:

 

case OPT_THREAD_HANDLING:

  {

    global_system_variables.thread_handling=

      find_type_or_exit(argument, &thread_handling_typelib, opt->name)-1;

    break;

  }

 

  

 

對參數初始化有興趣的可以具體的看下get_options這個函數,這裡就不詳細講解了。 我們來看下one_thread_scheduler和one_thread_per_connection_scheduler的源代碼,看下他們都做了些什麼?

 

void one_thread_scheduler(scheduler_functions* func)

{

  func->max_threads= 1;

#ifndef EMBEDDED_LIBRARY

  func->add_connection= handle_connection_in_main_thread;

#endif

  func->init_new_connection_thread= init_dummy;

  func->end_thread= no_threads_end;

}

 

void one_thread_per_connection_scheduler(scheduler_functions* func)

{

  func->max_threads= max_connections;

  func->add_connection= create_thread_to_handle_connection;

  func->end_thread= one_thread_per_connection_end;

}

 

  

 

   原來就是設置了一個結構體中scheduler_functions的參數,只不過這些參數是一些函數指針罷了,也就是說在具體的調用中,

 

只需要調用add_connection或end_thread即可,不需要知道到底是調用了哪個函數,這大概就是一種變形的多態性吧。

 

2.初始化網絡配置

 

   網絡配置比較簡單,就是設置端口,創建套接字,綁定端口,監聽端口。實現全部集中在network_init函數中,由於這個函數確

 

實沒什麼好講的,如果對socket不熟悉的話,可以去網上搜索下相關知識。這裡直接給出相應的偽代碼:

 

network_init

{

    set_ports; //設置端口號,#define MYSQL_PORT            3306

    socket;//創建套接字

    bind;       //綁定端口號

    listen;//監聽端口號

}

 

3.連接的方式

 

   進程間通信的方式不止是SOCKET,還有其他很多方式。Mysql支持三種連接方式:namepipe、socket和shared memory,

 

即命名管道、套接字和共享內存的方式。這三種方式是可以共存的。默認只使用套接字。

 

      TCP/IP套接字方式是MySQL在任何平台下都提供的連接方式,也是網絡中使用得最多的一種方式。這種方式在TCP/IP連接上建立

 

一個基於網絡的連接請求,一般情況下客戶端在一台服務器上,而MySQL實例在另一台服務器上,這兩台機器通過一個TCP/IP網絡連接。

 

例如,我可以在Windows服務器下請求一台遠程Linux服務器下的MySQL實例。

 

     在Windows 2000、Windows XP、Windows 2003和Windows Vista以及在此之後的Windows操作系統中,如果兩個需要通

 

信的進程在同一台服務器上,那麼可以使用命名管道,SQL Server數據庫默認安裝後的本地連接也使用命名管道。在MySQL數據庫中,

 

需在配置文件中啟用--enable-named-pipe選項。在MySQL 4.1之後的版本中,MySQL還提供了共享內存的連接方式,在配置文件中

 

添加--shared-memory。如果想使用共享內存的方式,在連接時,Mysql客戶端還必須使用-protocol=memory選項。

 

     啟動時可以通過下面的參數進行設置。

 

mysqld.exe --enable-named-pipe

mysqld.exe --shared-memory

 

   

 

除了在啟動時進行參數設置外,也可以通過修改MY.INI文件進行設置。我們來看下源碼中選擇連接方式的分支函數handle_connections_methods:

 

handle_connections_methods()

{

if (hPipe != INVALID_HANDLE_VALUE)

{

       handler_count++;

       if (pthread_create(&hThread,&connection_attrib,

               handle_connections_namedpipes, 0))

       {

      sql_print_warning("Can't create thread to handle named pipes");

      handler_count--;

    }

}

    if (have_tcpip && !opt_disable_networking)

  {

    handler_count++;

    if (pthread_create(&hThread,&connection_attrib,

               handle_connections_sockets, 0))

    {

      sql_print_warning("Can't create thread to handle TCP/IP");

      handler_count--;

    }

  }

 

if (opt_enable_shared_memory)

  {

    handler_count++;

    if (pthread_create(&hThread,&connection_attrib,

               handle_connections_shared_memory, 0))

    {

      sql_print_warning("Can't create thread to handle shared memory");

      handler_count--;

    }

  }

}

 

  

 

由於對於namepipe和memory share的通信方式不太了解,這裡只研究socket的通信方式。從代碼中可以看出,handle_connections_sockets便是socket的設置,我們就來看下它。

 

4.socket管理創建新線程socket管理其實比較簡單,直接給出其偽代碼:

 

handle_connections_sockets

{

    select; //監視socket文件描述符

    new_socket = accept;//處理到來的客戶端連接

    thd = new THD;創建THD類

    vio_tmp = vio_new(new_socket,VIO_TYPE_TCPIP, 0);   //初始化VIO結構體

    my_net_init(&thd->net, vio_tmp);//初始化thd的net結構體

    create_new_thread(thd);//為這個連接創建一個新的線程,如果是單線程模式的話,就不會創建一個新線程

}

 

  

 

首先是select函數進行監視socket端口,如果監控到有連接,則通過accept函數接受客戶端的連接,然後新建一個THD類,將連接參數全部設置到THD類的參數上,最後調用create_new_thread函數,這個函數便是重點。 我們進入這個函數,看下做了啥。

 

create_new_thread

{

    ++connection_count;//全局連接數自增

    thread_count++;    //全局線程數自增

    thread_scheduler.add_connection(thd);//真正創建線程

}

 

  

 

     So easy,首先將全局連接數+1,全局線程數+1,然後調用add_connection函數,這個函數就是我們在上面第一步設置連接的

 

線程數中,one_thread_scheduler和one_thread_per_connection_scheduler中設置的一個參數。這兩者的區別便是是否創建了

 

一個新的線程來處理到來的連接。one_thread_scheduler是單線程方式,木有新建線程。我們重點研究one_thread_per_connection_scheduler,其設置的add_connection函數為create_thread_to_handle_connection:

 

create_thread_to_handle_connection(THD *thd)

{

    thread_created++;

    threads.append(thd); // 創建線程數自增,並加入到threads鏈表上

    pthread_create(&thd->real_id,&connection_attrib,

                              handle_one_connection,

                              (void*) thd);//這就是真正創建線程的地方了,函數便是handle_one_connection

}

 

  

 

    可見,最後調用了pthread_create函數,這個函數便是創建一個新的線程,新線程的處理函數為handle_one_connection.

 

5.新線程處理流程

 

     新線程處理函數為handle_one_connection,到此位置,一個新的connection被一個新創建的線程所單獨處理。我們看下其中

 

是如何進行處理的。

 

handle_one_connection(void *arg)

{

    for (;;)

    {

        lex_start(thd); //初始化詞法分析結構體

        login_connection(thd); //用戶認證,失敗報錯

        prepare_new_connection_state(THD* thd);//Initialize THD to handle queries

         while (!net->error && net->vio != 0 &&   //循環處理command

           !(thd->killed == THD::KILL_CONNECTION))

        {

            if (do_command(thd))

              break;                   //處理失敗跳出

          }

          end_connection(thd);         //關閉連接

          close_connection(thd, 0, 1);

         thread_scheduler.end_thread(thd,1);//結束線程

                  return 0;

    }

}

 

  

 

    首先進行了詞法分析結構體的初始化,然後進行用戶認證,認證成功後通過do_command循環執行客戶端發過來的命令。

 

6.總結

 

     整個connection manager的流程十分清晰,單線程的連接一般很少使用,大多使用多線程方式。多線程連接中其實還涉及到線程緩沖

 

池的概念,即如果一個連接斷開後,其所創建的線程不會被銷毀掉,而是放到緩沖池中,等待下一個新的connection到來時,首先去線程

 

緩沖池查找是否有空閒的線程,有的話直接使用,木有的話才去創建新的線程來管理這個connection。這些屬於Thread Manage的內容,

 

下節進行Thread Manage的學習。

 

PS. 上周六迎來第一個雙休,結果還是被叫道公司改BUG,我忍。不過,BOSS,前幾個月的加班費啥時候發啊?不准拖欠民工工資…

 

踏著落葉,追尋著我的夢想。轉載請注明出處

 



摘自 心中無碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved