共計 5613 個字符,預計需要花費 15 分鐘才能閱讀完成。
這篇文章給大家介紹 Mysql 中 Connection Manager 的作用是什么,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
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 的內容,
關于 Mysql 中 Connection Manager 的作用是什么就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。