MySQL執行緒池總結(二)

天士夢發表於2015-07-01

上一篇主要講了執行緒池的原理和實現,感覺有點意猶未盡,這篇文章是對上篇文章的一個補充,主要圍繞以下兩點展開,one-connection-per-thread的實現方式以及執行緒池中epoll的使用。

one-connection-per-thread

根據scheduler_functions的模板,我們也可以列出one-connection-per-thread方式的幾個關鍵函式。

static scheduler_functions con_per_functions=

{ max_connection+1, // max_threads

NULL,

NULL,

NULL, // init

Init_new_connection_handler_thread, // init_new_connection_thread

create_thread_to_handle_connection, // add_connection

NULL, // thd_wait_begin

NULL, // thd_wait_end

NULL, // post_kill_notification

one_thread_per_connection_end, // end_thread

NULL // end

};

1.init_new_connection_handler_thread

這個介面比較簡單,主要是呼叫pthread_detach,將執行緒設定為detach狀態,執行緒結束後自動釋放所有資源。

2.create_thread_to_handle_connection

這個介面是處理新連線的介面,對於執行緒池而言,會從thread_id%group_size對應的group中獲取一個執行緒來處理,而one-connection-per-thread方式則會判斷是否有thread_cache可以使用,如果沒有則新建執行緒來處理。具體邏輯如下:

(1).判斷快取的執行緒數是否使用完(比較blocked_pthread_count 和wake_pthread大小)

(2).若還有快取執行緒,將thd加入waiting_thd_list的佇列,喚醒一個等待COND_thread_cache的執行緒

(3).若沒有,建立一個新的執行緒處理,執行緒的入口函式是do_handle_one_connection

(4).呼叫add_global_thread加入thd陣列。

3.do_handle_one_connection

這個介面被create_thread_to_handle_connection呼叫,處理請求的主要實現介面。

(1).迴圈呼叫do_command,從socket中讀取網路包,並且解析執行;

(2). 當遠端客戶端傳送關閉連線COMMAND(比如COM_QUIT,COM_SHUTDOWN)時,退出迴圈

(3).呼叫close_connection關閉連線(thd->disconnect());

(4).呼叫one_thread_per_connection_end函式,確認是否可以複用執行緒

(5).根據返回結果,確定退出工作執行緒還是繼續迴圈執行命令。

4.one_thread_per_connection_end

判斷是否可以複用執行緒(thread_cache)的主要函式,邏輯如下:

(1).呼叫remove_global_thread,移除執行緒對應的thd例項

(2).呼叫block_until_new_connection判斷是否可以重用thread

(3).判斷快取的執行緒是否超過閥值,若沒有,則blocked_pthread_count++;

(4).阻塞等待條件變數COND_thread_cache

(5).被喚醒後,表示有新的thd需要重用執行緒,將thd從waiting_thd_list中移除,使用thd初始化執行緒的thd->thread_stack

(6).呼叫add_global_thread加入thd陣列。

(7).如果可以重用,返回false,否則返回ture

執行緒池與epoll

在引入執行緒池之前,server層只有一個監聽執行緒,負責監聽mysql埠和本地unixsocket的請求,對於每個新的連線,都會分配一個獨立執行緒來處理,因此監聽執行緒的任務比較輕鬆,mysql通過poll或select方式來實現IO的多路複用。引入執行緒池後,除了server層的監聽執行緒,每個group都有一個監聽執行緒負責監聽group內的所有連線socket的連線請求,工作執行緒不負責監聽,只處理請求。對於overscribe為1000的執行緒池設定,每個監聽執行緒需要監聽1000個socket的請求,監聽執行緒採用epoll方式來實現監聽。

Select,poll,epoll都是IO多路複用機制,IO多路複用通過一種機制,可以監聽多個fd(描述符),比如socket,一旦某個fd就緒(讀就緒或寫就緒),能夠通知程式進行相應的讀寫操作。epoll相對於select和poll有了很大的改進,首先epoll通過epoll_ctl函式註冊,註冊時,將所有fd拷貝進核心,只拷貝一次不需要重複拷貝,而每次呼叫poll或select時,都需要將fd集合從使用者空間拷貝到核心空間(epoll通過epoll_wait進行等待);其次,epoll為每個描述符指定了一個回撥函式,當裝置就緒時,喚醒等待者,通過回撥函式將描述符加入到就緒連結串列,無需像select,poll方式採用輪詢方式;最後select預設只支援1024個fd,epoll則沒有限制,具體數字可以參考cat /proc/sys/fs/file-max的設定。epoll貫穿線上程池使用的過程中,下面我就epoll的建立,使用和銷燬生命週期來描述epoll線上程中是如何使用的。

  1. 執行緒池初始化,epoll通過epoll_create函式建立epoll檔案描述符,實現函式是thread_group_init;
  2. 埠監聽執行緒監聽到請求後,建立socket,並建立THD和connection物件,放在對應的group佇列中;
  3. 工作執行緒獲取該connection物件時,若還未登入,則進行登入驗證
  4. 若socket還未註冊到epoll,則呼叫epoll_ctl進行註冊,註冊方式是EPOLL_CTL_ADD,並將connection物件放入epoll_event結構體中
  5. 若是老連線的請求,仍然需要呼叫epoll_ctl註冊,註冊方式是EPOLL_CTL_MOD
  6. group內的監聽執行緒呼叫epoll_wait來監聽註冊的fd,epoll是一種同步IO方式,所以會進行等待
  7. 請求到來時,獲取epoll_event結構體中的connection,放入到group中的佇列
  8. 執行緒池銷燬時,呼叫thread_group_close將epoll關閉。

備註:

1.註冊在epoll的fd,若請求就緒,則將對應的event放入到events陣列,並將該fd的事務型別清空,因此對於老的連線請求,依然需要呼叫epoll_ctl(pollfd, EPOLL_CTL_MOD, fd, &ev)來註冊。

執行緒池函式呼叫關係

(1)建立epoll

tp_init->thread_group_init->tp_set_threadpool_size->io_poll_create->epoll_create

(2)關閉epoll

tp_end->thread_group_close->thread_group_destroy->close(pollfd)

(3)關聯socket描述符

handle_event->start_io->io_poll_associate_fd->io_poll_start_read->epoll_ctl

(4)處理連線請求

handle_event->threadpool_process_request->do_command->dispatch_command->mysql_parse->mysql_execute_command

(5)工作執行緒空閒時

worker_main->get_event->pthread_cond_timedwait

等待thread_pool_idle_timeout後,退出。

(6)監聽epoll

worker_main->get_event->listener->io_poll_wait->epoll_wait

(7)埠監聽執行緒

main->mysqld_main->handle_connections_sockets->poll

one-connection-per-thread函式呼叫關係

(1) 工作執行緒等待請求

handle_one_connection->do_handle_one_connection->do_command->

my_net_read->net_read_packet->net_read_packet_header->net_read_raw_loop->

vio_read->vio_socket_io_wait->vio_io_wait->poll

備註:與執行緒池的工作執行緒有監聽執行緒幫助其監聽請求不同,one-connection-per-thread方式的工作執行緒在空閒時,會呼叫poll阻塞等待網路包過來;

而執行緒池的工作執行緒只需要專心處理請求即可,所以使用也更充分。

(2)埠監聽執行緒

與執行緒池的(7)相同

參考文件

http://www.cnblogs.com/Anker/p/3265058.html

http://blog.csdn.net/zhanglu5227/article/details/7960677

相關文章