欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MySQL的線程池原理學(xué)習(xí)教程

 更新時(shí)間:2015年11月25日 16:09:20   投稿:goldensun  
這篇文章主要介紹了MySQL的線程池原理學(xué)習(xí)教程,包括線程池的函數(shù)調(diào)用與關(guān)鍵接口等重要知識(shí),非常推薦!需要的朋友可以參考下

線程池是Mysql5.6的一個(gè)核心功能,對(duì)于服務(wù)器應(yīng)用而言,無(wú)論是web應(yīng)用服務(wù)還是DB服務(wù),高并發(fā)請(qǐng)求始終是一個(gè)繞不開(kāi)的話題。當(dāng)有大量請(qǐng)求并發(fā)訪問(wèn)時(shí),一定伴隨著資源的不斷創(chuàng)建和釋放,導(dǎo)致資源利用率低,降低了服務(wù)質(zhì)量。線程池是一種通用的技術(shù),通過(guò)預(yù)先創(chuàng)建一定數(shù)量的線程,當(dāng)有請(qǐng)求達(dá)到時(shí),線程池分配一個(gè)線程提供服務(wù),請(qǐng)求結(jié)束后,該線程又去服務(wù)其他請(qǐng)求。 通過(guò)這種方式,避免了線程和內(nèi)存對(duì)象的頻繁創(chuàng)建和釋放,降低了服務(wù)端的并發(fā)度,減少了上下文切換和資源的競(jìng)爭(zhēng),提高資源利用效率。所有服務(wù)的線程池本質(zhì)都是位了提高資源利用效率,并且實(shí)現(xiàn)方式也大體相同。本文主要說(shuō)明Mysql線程池的實(shí)現(xiàn)原理。

在Mysql5.6出現(xiàn)以前,Mysql處理連接的方式是One-Connection-Per-Thread,即對(duì)于每一個(gè)數(shù)據(jù)庫(kù)連接,Mysql-Server都會(huì)創(chuàng)建一個(gè)獨(dú)立的線程服務(wù),請(qǐng)求結(jié)束后,銷(xiāo)毀線程。再來(lái)一個(gè)連接請(qǐng)求,則再創(chuàng)建一個(gè)連接,結(jié)束后再進(jìn)行銷(xiāo)毀。這種方式在高并發(fā)情況下,會(huì)導(dǎo)致線程的頻繁創(chuàng)建和釋放。當(dāng)然,通過(guò)thread-cache,我們可以將線程緩存起來(lái),以供下次使用,避免頻繁創(chuàng)建和釋放的問(wèn)題,但是無(wú)法解決高連接數(shù)的問(wèn)題。One-Connection-Per-Thread方式隨著連接數(shù)暴增,導(dǎo)致需要?jiǎng)?chuàng)建同樣多的服務(wù)線程,高并發(fā)線程意味著高的內(nèi)存消耗,更多的上下文切換(cpu cache命中率降低)以及更多的資源競(jìng)爭(zhēng),導(dǎo)致服務(wù)出現(xiàn)抖動(dòng)。相對(duì)于One-Thread-Per-Connection方式,一個(gè)線程對(duì)應(yīng)一個(gè)連接,Thread-Pool實(shí)現(xiàn)方式中,線程處理的最小單位是statement(語(yǔ)句),一個(gè)線程可以處理多個(gè)連接的請(qǐng)求。這樣,在保證充分利用硬件資源情況下(合理設(shè)置線程池大小),可以避免瞬間連接數(shù)暴增導(dǎo)致的服務(wù)器抖動(dòng)。

 

調(diào)度方式實(shí)現(xiàn)

Mysql-Server同時(shí)支持3種連接管理方式,包括No-Threads,One-Thread-Per-Connection和Pool-Threads。No-Threads表示處理連接使用主線程處理,不額外創(chuàng)建線程,這種方式主要用于調(diào)試;One-Thread-Per-Connection是線程池出現(xiàn)以前最常用的方式,為每一個(gè)連接創(chuàng)建一個(gè)線程服務(wù);Pool-Threads則是本文所討論的線程池方式。Mysql-Server通過(guò)一組函數(shù)指針來(lái)同時(shí)支持3種連接管理方式,對(duì)于特定的方式,將函數(shù)指針設(shè)置成特定的回調(diào)函數(shù),連接管理方式通過(guò)thread_handling參數(shù)控制,代碼如下:

if (thread_handling <= SCHEDULER_ONE_THREAD_PER_CONNECTION)  
 one_thread_per_connection_scheduler(thread_scheduler,
                   &max_connections,
                   &connection_count);
else if (thread_handling == SCHEDULER_NO_THREADS)
 one_thread_scheduler(thread_scheduler);
else                
 pool_of_threads_scheduler(thread_scheduler, &max_connections,&connection_count);
 

連接管理流程

通過(guò)poll監(jiān)聽(tīng)mysql端口的連接請(qǐng)求
收到連接后,調(diào)用accept接口,創(chuàng)建通信socket
初始化thd實(shí)例,vio對(duì)象等
根據(jù)thread_handling方式設(shè)置,初始化thd實(shí)例的scheduler函數(shù)指針
調(diào)用scheduler特定的add_connection函數(shù)新建連接
下面代碼展示了scheduler_functions模板和線程池對(duì)模板回調(diào)函數(shù)的實(shí)現(xiàn),這個(gè)是多種連接管理的核心。

struct scheduler_functions 
{ 
uint max_threads;
 
uint *connection_count; 
 
ulong *max_connections; 
 
bool (*init)(void); 
 
bool (*init_new_connection_thread)(void);
 
void (*add_connection)(THD *thd);
 
void (*thd_wait_begin)(THD *thd, int wait_type);
 
void (*thd_wait_end)(THD *thd);
 
void (*post_kill_notification)(THD *thd);
 
bool (*end_thread)(THD *thd, bool cache_thread);
 
void (*end)(void);
};

static scheduler_functions tp_scheduler_functions=
 
{ 
0, 
// max_threads
NULL,
NULL, 
tp_init, 
// init
NULL, 
// init_new_connection_thread
tp_add_connection, 
// add_connection
tp_wait_begin, 
// thd_wait_begin 
tp_wait_end, 
// thd_wait_end
tp_post_kill_notification, 
// post_kill_notification 
NULL, 
// end_thread
tp_end 
// end
 
};

線程池的相關(guān)參數(shù)

  • thread_handling:表示線程池模型。
  • thread_pool_size:表示線程池的group個(gè)數(shù),一般設(shè)置為當(dāng)前CPU核心數(shù)目。理想情況下,一個(gè)group一個(gè)活躍的工作線程,達(dá)到充分利用CPU的目的。
  • thread_pool_stall_limit:用于timer線程定期檢查group是否“停滯”,參數(shù)表示檢測(cè)的間隔。
  • thread_pool_idle_timeout:當(dāng)一個(gè)worker空閑一段時(shí)間后會(huì)自動(dòng)退出,保證線程池中的工作線程在滿足請(qǐng)求的情況下,保持比較低的水平。
  • thread_pool_oversubscribe:該參數(shù)用于控制CPU核心上“超頻”的線程數(shù)。這個(gè)參數(shù)設(shè)置值不含listen線程計(jì)數(shù)。
  • threadpool_high_prio_mode:表示優(yōu)先隊(duì)列的模式。

線程池實(shí)現(xiàn)

上面描述了Mysql-Server如何管理連接,這節(jié)重點(diǎn)描述線程池的實(shí)現(xiàn)框架,以及關(guān)鍵接口。如圖1

20151125160402636.png (868×450)

每一個(gè)綠色的方框代表一個(gè)group,group數(shù)目由thread_pool_size參數(shù)決定。每個(gè)group包含一個(gè)優(yōu)先隊(duì)列和普通隊(duì)列,包含一個(gè)listener線程和若干個(gè)工作線程,listener線程和worker線程可以動(dòng)態(tài)轉(zhuǎn)換,worker線程數(shù)目由工作負(fù)載決定,同時(shí)受到thread_pool_oversubscribe設(shè)置影響。此外,整個(gè)線程池有一個(gè)timer線程監(jiān)控group,防止group“停滯”。

關(guān)鍵接口

1. tp_add_connection[處理新連接]

1) 創(chuàng)建一個(gè)connection對(duì)象

2) 根據(jù)thread_id%group_count確定connection分配到哪個(gè)group

3) 將connection放進(jìn)對(duì)應(yīng)group的隊(duì)列

4) 如果當(dāng)前活躍線程數(shù)為0,則創(chuàng)建一個(gè)工作線程

2. worker_main[工作線程]

1) 調(diào)用get_event獲取請(qǐng)求

2) 如果存在請(qǐng)求,則調(diào)用handle_event進(jìn)行處理

3) 否則,表示隊(duì)列中已經(jīng)沒(méi)有請(qǐng)求,退出結(jié)束。

3. get_event[獲取請(qǐng)求]

1) 獲取一個(gè)連接請(qǐng)求

2) 如果存在,則立即返回,結(jié)束

3) 若此時(shí)group內(nèi)沒(méi)有l(wèi)istener,則線程轉(zhuǎn)換為listener線程,阻塞等待

4) 若存在listener,則將線程加入等待隊(duì)列頭部

5) 線程休眠指定的時(shí)間(thread_pool_idle_timeout)

6) 如果依然沒(méi)有被喚醒,是超時(shí),則線程結(jié)束,結(jié)束退出

7) 否則,表示隊(duì)列里有連接請(qǐng)求到來(lái),跳轉(zhuǎn)1

備注:獲取連接請(qǐng)求前,會(huì)判斷當(dāng)前的活躍線程數(shù)是否超過(guò)了

thread_pool_oversubscribe+1,若超過(guò)了,則將線程進(jìn)入休眠狀態(tài)。

4. handle_event[處理請(qǐng)求]

1) 判斷連接是否進(jìn)行登錄驗(yàn)證,若沒(méi)有,則進(jìn)行登錄驗(yàn)證

2) 關(guān)聯(lián)thd實(shí)例信息

3) 獲取網(wǎng)絡(luò)數(shù)據(jù)包,分析請(qǐng)求

4) 調(diào)用do_command函數(shù)循環(huán)處理請(qǐng)求

5) 獲取thd實(shí)例的套接字句柄,判斷句柄是否在epoll的監(jiān)聽(tīng)列表中

6) 若沒(méi)有,調(diào)用epoll_ctl進(jìn)行關(guān)聯(lián)

7) 結(jié)束

5.listener[監(jiān)聽(tīng)線程]

1) 調(diào)用epoll_wait進(jìn)行對(duì)group關(guān)聯(lián)的套接字監(jiān)聽(tīng),阻塞等待

2) 若請(qǐng)求到來(lái),從阻塞中恢復(fù)

3) 根據(jù)連接的優(yōu)先級(jí)別,確定是放入普通隊(duì)列還是優(yōu)先隊(duì)列

4) 判斷隊(duì)列中任務(wù)是否為空

5) 若隊(duì)列為空,則listener轉(zhuǎn)換為worker線程

6) 若group內(nèi)沒(méi)有活躍線程,則喚醒一個(gè)線程

備注:這里epoll_wait監(jiān)聽(tīng)group內(nèi)所有連接的套接字,然后將監(jiān)聽(tīng)到的連接

請(qǐng)求push到隊(duì)列,worker線程從隊(duì)列中獲取任務(wù),然后執(zhí)行。

6. timer_thread[監(jiān)控線程]

1) 若沒(méi)有l(wèi)istener線程,并且最近沒(méi)有io_event事件

2) 則創(chuàng)建一個(gè)喚醒或創(chuàng)建一個(gè)工作線程

3) 若group最近一段時(shí)間沒(méi)有處理請(qǐng)求,并且隊(duì)列里面有請(qǐng)求,則

4) 表示group已經(jīng)stall,則喚醒或創(chuàng)建線程

5)檢查是否有連接超時(shí)

備注:timer線程通過(guò)調(diào)用check_stall判斷group是否處于stall狀態(tài),通過(guò)調(diào)用timeout_check檢查客戶端連接是否超時(shí)。

7.tp_wait_begin[進(jìn)入等待狀態(tài)流程]

1) active_thread_count減1,waiting_thread_count加1

2)設(shè)置connection->waiting= true

3) 若活躍線程數(shù)為0,并且任務(wù)隊(duì)列不為空,或者沒(méi)有監(jiān)聽(tīng)線程,則

4) 喚醒或創(chuàng)建一個(gè)線程

8.tp_wait_end[結(jié)束等待狀態(tài)流程]

1) 設(shè)置connection的waiting狀態(tài)為false

2) active_thread_count加1,waiting_thread_count減1

備注:

1)waiting_threads這個(gè)list里面的線程是空閑線程,并非等待線程,所謂空閑線程是隨時(shí)可以處理任務(wù)的線程,而等待線程則是因?yàn)榈却i,或等待io操作等無(wú)法處理任務(wù)的線程。

2)tp_wait_begin和tp_wait_end的主要作用是由于匯報(bào)狀態(tài),即使更新active_thread_count和waiting_thread_count的信息。

9. tp_init/tp_end

分別調(diào)用thread_group_init和thread_group_close來(lái)初始化和銷(xiāo)毀線程池

 

線程池與連接池

連接池通常實(shí)現(xiàn)在Client端,是指應(yīng)用(客戶端)創(chuàng)建預(yù)先創(chuàng)建一定的連接,利用這些連接服務(wù)于客戶端所有的DB請(qǐng)求。如果某一個(gè)時(shí)刻,空閑的連接數(shù)小于DB的請(qǐng)求數(shù),則需要將請(qǐng)求排隊(duì),等待空閑連接處理。通過(guò)連接池可以復(fù)用連接,避免連接的頻繁創(chuàng)建和釋放,從而減少請(qǐng)求的平均響應(yīng)時(shí)間,并且在請(qǐng)求繁忙時(shí),通過(guò)請(qǐng)求排隊(duì),可以緩沖應(yīng)用對(duì)DB的沖擊。線程池實(shí)現(xiàn)在server端,通過(guò)創(chuàng)建一定數(shù)量的線程服務(wù)DB請(qǐng)求,相對(duì)于one-conection-per-thread的一個(gè)線程服務(wù)一個(gè)連接的方式,線程池服務(wù)的最小單位是語(yǔ)句,即一個(gè)線程可以對(duì)應(yīng)多個(gè)活躍的連接。通過(guò)線程池,可以將server端的服務(wù)線程數(shù)控制在一定的范圍,減少了系統(tǒng)資源的競(jìng)爭(zhēng)和線程上下文切換帶來(lái)的消耗,同時(shí)也避免出現(xiàn)高連接數(shù)導(dǎo)致的高并發(fā)問(wèn)題。連接池和線程池相輔相成,通過(guò)連接池可以減少連接的創(chuàng)建和釋放,提高請(qǐng)求的平均響應(yīng)時(shí)間,并能很好地控制一個(gè)應(yīng)用的DB連接數(shù),但無(wú)法控制整個(gè)應(yīng)用集群的連接數(shù)規(guī)模,從而導(dǎo)致高連接數(shù),通過(guò)線程池則可以很好地應(yīng)對(duì)高連接數(shù),保證server端能提供穩(wěn)定的服務(wù)。如圖2所示,每個(gè)web-server端維護(hù)了3個(gè)連接的連接池,對(duì)于連接池的每個(gè)連接實(shí)際不是獨(dú)占db-server的一個(gè)worker,而是可能與其他連接共享。這里假設(shè)db-server只有3個(gè)group,每個(gè)group只有一個(gè)worker,每個(gè)worker處理了2個(gè)連接的請(qǐng)求。

20151125160452103.png (767×468)

線程池優(yōu)化

1.調(diào)度死鎖解決

引入線程池解決了多線程高并發(fā)的問(wèn)題,但也帶來(lái)一個(gè)隱患。假設(shè),A,B兩個(gè)事務(wù)被分配到不同的group中執(zhí)行,A事務(wù)已經(jīng)開(kāi)始,并且持有鎖,但由于A所在的group比較繁忙,導(dǎo)致A執(zhí)行一條語(yǔ)句后,不能立即獲得調(diào)度執(zhí)行;而B(niǎo)事務(wù)依賴(lài)A事務(wù)釋放鎖資源,雖然B事務(wù)可以被調(diào)度起來(lái),但由于無(wú)法獲得鎖資源,導(dǎo)致仍然需要等待,這就是所謂的調(diào)度死鎖。由于一個(gè)group會(huì)同時(shí)處理多個(gè)連接,但多個(gè)連接不是對(duì)等的。比如,有的連接是第一次發(fā)送請(qǐng)求;而有的連接對(duì)應(yīng)的事務(wù)已經(jīng)開(kāi)啟,并且持有了部分鎖資源。為了減少鎖資源爭(zhēng)用,后者顯然應(yīng)該比前者優(yōu)先處理,以達(dá)到盡早釋放鎖資源的目的。因此在group里面,可以添加一個(gè)優(yōu)先級(jí)隊(duì)列,將已經(jīng)持有鎖的連接,或者已經(jīng)開(kāi)啟的事務(wù)的連接發(fā)起的請(qǐng)求放入優(yōu)先隊(duì)列,工作線程首先從優(yōu)先隊(duì)列獲取任務(wù)執(zhí)行。

2.大查詢處理

假設(shè)一種場(chǎng)景,某個(gè)group里面的連接都是大查詢,那么group里面的工作線程數(shù)很快就會(huì)達(dá)到thread_pool_oversubscribe參數(shù)設(shè)置值,對(duì)于后續(xù)的連接請(qǐng)求,則會(huì)響應(yīng)不及時(shí)(沒(méi)有更多的連接來(lái)處理),這時(shí)候group就發(fā)生了stall。通過(guò)前面分析知道,timer線程會(huì)定期檢查這種情況,并創(chuàng)建一個(gè)新的worker線程來(lái)處理請(qǐng)求。如果長(zhǎng)查詢來(lái)源于業(yè)務(wù)請(qǐng)求,則此時(shí)所有g(shù)roup都面臨這種問(wèn)題,此時(shí)主機(jī)可能會(huì)由于負(fù)載過(guò)大,導(dǎo)致hang住的情況。這種情況線程池本身無(wú)能為力,因?yàn)樵搭^可能是爛SQL并發(fā),或者SQL沒(méi)有走對(duì)執(zhí)行計(jì)劃導(dǎo)致,通過(guò)其他方法,比如SQL高低水位限流或者SQL過(guò)濾手段可以應(yīng)急處理。但是,還有另外一種情況,就是dump任務(wù)。很多下游依賴(lài)于數(shù)據(jù)庫(kù)的原始數(shù)據(jù),通常通過(guò)dump命令將數(shù)據(jù)拉到下游,而這種dump任務(wù)通常都是耗時(shí)比較長(zhǎng),所以也可以認(rèn)為是大查詢。如果dump任務(wù)集中在一個(gè)group內(nèi),并導(dǎo)致其他正常業(yè)務(wù)請(qǐng)求無(wú)法立即響應(yīng),這個(gè)是不能容忍的,因?yàn)榇藭r(shí)數(shù)據(jù)庫(kù)并沒(méi)有壓力,只是因?yàn)椴捎昧司€程池策略,才導(dǎo)致了請(qǐng)求響應(yīng)不及時(shí),為了解決這個(gè)問(wèn)題,我們將group中處理dump任務(wù)的線程不計(jì)入thread_pool_oversubscribe累計(jì)值,避免上述問(wèn)題。

one-connection-per-thread

根據(jù)scheduler_functions的模板,我們也可以列出one-connection-per-thread方式的幾個(gè)關(guān)鍵函數(shù)。

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

這個(gè)接口比較簡(jiǎn)單,主要是調(diào)用pthread_detach,將線程設(shè)置為detach狀態(tài),線程結(jié)束后自動(dòng)釋放所有資源。

2.create_thread_to_handle_connection

這個(gè)接口是處理新連接的接口,對(duì)于線程池而言,會(huì)從thread_id%group_size對(duì)應(yīng)的group中獲取一個(gè)線程來(lái)處理,而one-connection-per-thread方式則會(huì)判斷是否有thread_cache可以使用,如果沒(méi)有則新建線程來(lái)處理。具體邏輯如下:

(1).判斷緩存的線程數(shù)是否使用完(比較blocked_pthread_count 和wake_pthread大小)

(2).若還有緩存線程,將thd加入waiting_thd_list的隊(duì)列,喚醒一個(gè)等待COND_thread_cache的線程

(3).若沒(méi)有,創(chuàng)建一個(gè)新的線程處理,線程的入口函數(shù)是do_handle_one_connection

(4).調(diào)用add_global_thread加入thd數(shù)組。

3.do_handle_one_connection

這個(gè)接口被create_thread_to_handle_connection調(diào)用,處理請(qǐng)求的主要實(shí)現(xiàn)接口。

(1).循環(huán)調(diào)用do_command,從socket中讀取網(wǎng)絡(luò)包,并且解析執(zhí)行;

(2). 當(dāng)遠(yuǎn)程客戶端發(fā)送關(guān)閉連接COMMAND(比如COM_QUIT,COM_SHUTDOWN)時(shí),退出循環(huán)

(3).調(diào)用close_connection關(guān)閉連接(thd->disconnect());

(4).調(diào)用one_thread_per_connection_end函數(shù),確認(rèn)是否可以復(fù)用線程

(5).根據(jù)返回結(jié)果,確定退出工作線程還是繼續(xù)循環(huán)執(zhí)行命令。

4.one_thread_per_connection_end

判斷是否可以復(fù)用線程(thread_cache)的主要函數(shù),邏輯如下:

(1).調(diào)用remove_global_thread,移除線程對(duì)應(yīng)的thd實(shí)例

(2).調(diào)用block_until_new_connection判斷是否可以重用thread

(3).判斷緩存的線程是否超過(guò)閥值,若沒(méi)有,則blocked_pthread_count++;

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

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

(6).調(diào)用add_global_thread加入thd數(shù)組。

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

 

線程池與epoll

在引入線程池之前,server層只有一個(gè)監(jiān)聽(tīng)線程,負(fù)責(zé)監(jiān)聽(tīng)mysql端口和本地unixsocket的請(qǐng)求,對(duì)于每個(gè)新的連接,都會(huì)分配一個(gè)獨(dú)立線程來(lái)處理,因此監(jiān)聽(tīng)線程的任務(wù)比較輕松,mysql通過(guò)poll或select方式來(lái)實(shí)現(xiàn)IO的多路復(fù)用。引入線程池后,除了server層的監(jiān)聽(tīng)線程,每個(gè)group都有一個(gè)監(jiān)聽(tīng)線程負(fù)責(zé)監(jiān)聽(tīng)group內(nèi)的所有連接socket的連接請(qǐng)求,工作線程不負(fù)責(zé)監(jiān)聽(tīng),只處理請(qǐng)求。對(duì)于overscribe為1000的線程池設(shè)置,每個(gè)監(jiān)聽(tīng)線程需要監(jiān)聽(tīng)1000個(gè)socket的請(qǐng)求,監(jiān)聽(tīng)線程采用epoll方式來(lái)實(shí)現(xiàn)監(jiān)聽(tīng)。

Select,poll,epoll都是IO多路復(fù)用機(jī)制,IO多路復(fù)用通過(guò)一種機(jī)制,可以監(jiān)聽(tīng)多個(gè)fd(描述符),比如socket,一旦某個(gè)fd就緒(讀就緒或?qū)懢途w),能夠通知程序進(jìn)行相應(yīng)的讀寫(xiě)操作。epoll相對(duì)于select和poll有了很大的改進(jìn),首先epoll通過(guò)epoll_ctl函數(shù)注冊(cè),注冊(cè)時(shí),將所有fd拷貝進(jìn)內(nèi)核,只拷貝一次不需要重復(fù)拷貝,而每次調(diào)用poll或select時(shí),都需要將fd集合從用戶空間拷貝到內(nèi)核空間(epoll通過(guò)epoll_wait進(jìn)行等待);其次,epoll為每個(gè)描述符指定了一個(gè)回調(diào)函數(shù),當(dāng)設(shè)備就緒時(shí),喚醒等待者,通過(guò)回調(diào)函數(shù)將描述符加入到就緒鏈表,無(wú)需像select,poll方式采用輪詢方式;最后select默認(rèn)只支持1024個(gè)fd,epoll則沒(méi)有限制,具體數(shù)字可以參考cat /proc/sys/fs/file-max的設(shè)置。epoll貫穿在線程池使用的過(guò)程中,下面我就epoll的創(chuàng)建,使用和銷(xiāo)毀生命周期來(lái)描述epoll在線程中是如何使用的。

線程池初始化,epoll通過(guò)epoll_create函數(shù)創(chuàng)建epoll文件描述符,實(shí)現(xiàn)函數(shù)是thread_group_init;
端口監(jiān)聽(tīng)線程監(jiān)聽(tīng)到請(qǐng)求后,創(chuàng)建socket,并創(chuàng)建THD和connection對(duì)象,放在對(duì)應(yīng)的group隊(duì)列中;
工作線程獲取該connection對(duì)象時(shí),若還未登錄,則進(jìn)行登錄驗(yàn)證
若socket還未注冊(cè)到epoll,則調(diào)用epoll_ctl進(jìn)行注冊(cè),注冊(cè)方式是EPOLL_CTL_ADD,并將connection對(duì)象放入epoll_event結(jié)構(gòu)體中
若是老連接的請(qǐng)求,仍然需要調(diào)用epoll_ctl注冊(cè),注冊(cè)方式是EPOLL_CTL_MOD
group內(nèi)的監(jiān)聽(tīng)線程調(diào)用epoll_wait來(lái)監(jiān)聽(tīng)注冊(cè)的fd,epoll是一種同步IO方式,所以會(huì)進(jìn)行等待
請(qǐng)求到來(lái)時(shí),獲取epoll_event結(jié)構(gòu)體中的connection,放入到group中的隊(duì)列
線程池銷(xiāo)毀時(shí),調(diào)用thread_group_close將epoll關(guān)閉。
備注:

1.注冊(cè)在epoll的fd,若請(qǐng)求就緒,則將對(duì)應(yīng)的event放入到events數(shù)組,并將該fd的事務(wù)類(lèi)型清空,因此對(duì)于老的連接請(qǐng)求,依然需要調(diào)用epoll_ctl(pollfd, EPOLL_CTL_MOD, fd, &ev)來(lái)注冊(cè)。

 

線程池函數(shù)調(diào)用關(guān)系

(1)創(chuàng)建epoll

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

(2)關(guān)閉epoll

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

(3)關(guān)聯(lián)socket描述符

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

(4)處理連接請(qǐng)求

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

(5)工作線程空閑時(shí)

worker_main->get_event->pthread_cond_timedwait

等待thread_pool_idle_timeout后,退出。

(6)監(jiān)聽(tīng)epoll

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

(7)端口監(jiān)聽(tīng)線程

main->mysqld_main->handle_connections_sockets->poll

 

one-connection-per-thread函數(shù)調(diào)用關(guān)系

(1) 工作線程等待請(qǐng)求

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

備注:與線程池的工作線程有監(jiān)聽(tīng)線程幫助其監(jiān)聽(tīng)請(qǐng)求不同,one-connection-per-thread方式的工作線程在空閑時(shí),會(huì)調(diào)用poll阻塞等待網(wǎng)絡(luò)包過(guò)來(lái);

而線程池的工作線程只需要專(zhuān)心處理請(qǐng)求即可,所以使用也更充分。

(2)端口監(jiān)聽(tīng)線程
與線程池的(7)相同

相關(guān)文章

  • MySQL觸發(fā)器簡(jiǎn)單用法示例

    MySQL觸發(fā)器簡(jiǎn)單用法示例

    這篇文章主要介紹了MySQL觸發(fā)器簡(jiǎn)單用法,結(jié)合實(shí)例形式分析了mysql觸發(fā)器的創(chuàng)建、執(zhí)行、查看、刪除等相關(guān)操作技巧,需要的朋友可以參考下
    2018-03-03
  • MariaDB(Mysql分支)my.cnf配置文件中文注釋版

    MariaDB(Mysql分支)my.cnf配置文件中文注釋版

    這篇文章主要介紹了MariaDB my.cnf配置文件中文注釋版,MariaDB是Mysql的一個(gè)分支,完全兼容Mysql,需要的朋友可以參考下
    2014-06-06
  • 最新評(píng)論