one-connection-per-thread
從事成都移動(dòng)機(jī)房,服務(wù)器租用,云主機(jī),網(wǎng)站空間,域名申請(qǐng),CDN,網(wǎng)絡(luò)代維等服務(wù)。
根據(jù)scheduler_functions的模板,我們也可以列出one-connection-per-thread方式的幾個(gè)關(guān)鍵函數(shù)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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è)線程來處理,而one-connection-per-thread方式則會(huì)判斷是否有thread_cache可以使用,如果沒有則新建線程來處理。具體邏輯如下:
(1).判斷緩存的線程數(shù)是否使用完(比較blocked_pthread_count 和wake_pthread大小)
(2).若還有緩存線程,將thd加入waiting_thd_list的隊(duì)列,喚醒一個(gè)等待COND_thread_cache的線程
(3).若沒有,創(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).判斷緩存的線程是否超過閥值,若沒有,則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)聽線程,負(fù)責(zé)監(jiān)聽mysql端口和本地unixsocket的請(qǐng)求,對(duì)于每個(gè)新的連接,都會(huì)分配一個(gè)獨(dú)立線程來處理,因此監(jiān)聽線程的任務(wù)比較輕松,mysql通過poll或select方式來實(shí)現(xiàn)IO的多路復(fù)用。引入線程池后,除了server層的監(jiān)聽線程,每個(gè)group都有一個(gè)監(jiān)聽線程負(fù)責(zé)監(jiān)聽group內(nèi)的所有連接socket的連接請(qǐng)求,工作線程不負(fù)責(zé)監(jiān)聽,只處理請(qǐng)求。對(duì)于overscribe為1000的線程池設(shè)置,每個(gè)監(jiān)聽線程需要監(jiān)聽1000個(gè)socket的請(qǐng)求,監(jiān)聽線程采用epoll方式來實(shí)現(xiàn)監(jiān)聽。
Select,poll,epoll都是IO多路復(fù)用機(jī)制,IO多路復(fù)用通過一種機(jī)制,可以監(jiān)聽多個(gè)fd(描述符),比如socket,一旦某個(gè)fd就緒(讀就緒或?qū)懢途w),能夠通知程序進(jìn)行相應(yīng)的讀寫操作。epoll相對(duì)于select和poll有了很大的改進(jìn),首先epoll通過epoll_ctl函數(shù)注冊(cè),注冊(cè)時(shí),將所有fd拷貝進(jìn)內(nèi)核,只拷貝一次不需要重復(fù)拷貝,而每次調(diào)用poll或select時(shí),都需要將fd集合從用戶空間拷貝到內(nèi)核空間(epoll通過epoll_wait進(jìn)行等待);其次,epoll為每個(gè)描述符指定了一個(gè)回調(diào)函數(shù),當(dāng)設(shè)備就緒時(shí),喚醒等待者,通過回調(diào)函數(shù)將描述符加入到就緒鏈表,無需像select,poll方式采用輪詢方式;最后select默認(rèn)只支持1024個(gè)fd,epoll則沒有限制,具體數(shù)字可以參考cat /proc/sys/fs/file-max的設(shè)置。epoll貫穿在線程池使用的過程中,下面我就epoll的創(chuàng)建,使用和銷毀生命周期來描述epoll在線程中是如何使用的。
線程池初始化,epoll通過epoll_create函數(shù)創(chuàng)建epoll文件描述符,實(shí)現(xiàn)函數(shù)是thread_group_init;
端口監(jiān)聽線程監(jiān)聽到請(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)聽線程調(diào)用epoll_wait來監(jiān)聽注冊(cè)的fd,epoll是一種同步IO方式,所以會(huì)進(jìn)行等待
請(qǐng)求到來時(shí),獲取epoll_event結(jié)構(gòu)體中的connection,放入到group中的隊(duì)列
線程池銷毀時(shí),調(diào)用thread_group_close將epoll關(guān)閉。
備注:
1.注冊(cè)在epoll的fd,若請(qǐng)求就緒,則將對(duì)應(yīng)的event放入到events數(shù)組,并將該fd的事務(wù)類型清空,因此對(duì)于老的連接請(qǐng)求,依然需要調(diào)用epoll_ctl(pollfd, EPOLL_CTL_MOD, fd, ev)來注冊(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)聽epoll
worker_main-get_event-listener-io_poll_wait-epoll_wait
(7)端口監(jiān)聽線程
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)聽線程幫助其監(jiān)聽請(qǐng)求不同,one-connection-per-thread方式的工作線程在空閑時(shí),會(huì)調(diào)用poll阻塞等待網(wǎng)絡(luò)包過來;
而線程池的工作線程只需要專心處理請(qǐng)求即可,所以使用也更充分。
(2)端口監(jiān)聽線程
與線程池的(7)相同
參考文檔
MySQL的查詢使用的是線程池。當(dāng)有大量請(qǐng)求并發(fā)訪問時(shí),一定伴隨著資源的不斷創(chuàng)建和釋放,導(dǎo)致資源利用率低,降低了服務(wù)質(zhì)量。線程池技術(shù),預(yù)先會(huì)創(chuàng)建一定數(shù)量的線程,當(dāng)有請(qǐng)求達(dá)到時(shí),線程池分配一個(gè)線程提供服務(wù),請(qǐng)求結(jié)束后,該線程又去服務(wù)其他請(qǐng)求。 通過這種方式,避免了線程和內(nèi)存對(duì)象的頻繁創(chuàng)建和釋放,降低了服務(wù)端的并發(fā)度,減少了上下文切換和資源的競(jìng)爭(zhēng),提高資源利用效率。在MySQL早期的版本中,處理連接的方式是One-Connection-Per-Thread,即對(duì)于每一個(gè)數(shù)據(jù)庫(kù)連接,MySQL-Server都會(huì)創(chuàng)建一個(gè)獨(dú)立的線程服務(wù),請(qǐng)求結(jié)束后,銷毀線程。再來一個(gè)連接請(qǐng)求,則再創(chuàng)建一個(gè)連接,結(jié)束后再進(jìn)行銷毀。但是,這種方式在高并發(fā)情況下,會(huì)導(dǎo)致線程的頻繁創(chuàng)建和釋放。當(dāng)然,通過thread-cache,我們可以將線程緩存起來,以供下次使用,避免頻繁創(chuàng)建和釋放的問題,但是無法解決高連接數(shù)的問題。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(語句),一個(gè)線程可以處理多個(gè)連接的請(qǐng)求。這樣,在保證充分利用硬件資源情況下(合理設(shè)置線程池大小),可以避免瞬間連接數(shù)暴增導(dǎo)致的服務(wù)器抖動(dòng)。
用Fsocket獲取數(shù)據(jù)時(shí)能夠控制超時(shí)的。
如果用
File_get_contents($url);
可以臨時(shí)設(shè)定環(huán)境變量:
設(shè)定默認(rèn)socket超時(shí)時(shí)間
ini_set("default_socket_timeout", 3);養(yǎng)成好習(xí)慣,使用fsocket獲取數(shù)據(jù)。
如果使用Curl,也可以在Curl中控制超時(shí)時(shí)間:
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
PHP中mysql函數(shù)是不提供類似mysql超時(shí)選項(xiàng)的,但是php.ini的mysql.connect_timeout可設(shè)置
; Maximum time (in seconds) for connect timeout. -1 means nolimit
mysql.connect_timeout = 60
也可以在php腳本中調(diào)用設(shè)置ini_set();
在MySQL 8.0 之前, 我們假設(shè)一下有一條爛SQL,
mysqlselect * from t1 order by rand() ;
以多個(gè)線程在跑,導(dǎo)致CPU被跑滿了,其他的請(qǐng)求只能被阻塞進(jìn)不來。那這種情況怎么辦?
大概有以下幾種解決辦法:
設(shè)置max_execution_time 來阻止太長(zhǎng)的讀SQL。那可能存在的問題是會(huì)把所有長(zhǎng)SQL都給KILL 掉。有些必須要執(zhí)行很長(zhǎng)時(shí)間的也會(huì)被誤殺。
自己寫個(gè)腳本檢測(cè)這類語句,比如order by rand(), 超過一定時(shí)間用Kill query thread_id 給殺掉。
那能不能不要?dú)⒌舳屗_\(yùn)行,但是又不影響其他的請(qǐng)求呢?
那mysql 8.0 引入的資源組(resource group,后面簡(jiǎn)寫微RG)可以基本上解決這類問題。
比如我可以用 RG 來在SQL層面給他限制在特定的一個(gè)CPU核上,這樣我就不管他,讓他繼續(xù)運(yùn)行,如果有新的此類語句,讓他排隊(duì)好了。
為什么說基本呢?目前只能綁定CPU資源,其他的暫時(shí)不行。
那我來演示下如何使用RG。
創(chuàng)建一個(gè)資源組user_ytt. 這里解釋下各個(gè)參數(shù)的含義,
type = user 表示這是一個(gè)用戶態(tài)線程,也就是前臺(tái)的請(qǐng)求線程。如果type=system,表示后臺(tái)線程,用來限制mysql自己的線程,比如Innodb purge thread,innodb read thread等等。
vcpu 代表cpu的邏輯核數(shù),這里0-1代表前兩個(gè)核被綁定到這個(gè)RG。可以用lscpu,top等列出自己的CPU相關(guān)信息。
thread_priority 設(shè)置優(yōu)先級(jí)。user 級(jí)優(yōu)先級(jí)設(shè)置大于0。
mysqlmysql create resource group user_ytt type = user ?vcpu = 0-1 thread_priority=19 enable;Query OK, 0 rows affected (0.03 sec)
RG相關(guān)信息可以從 information_schema.resource_groups 系統(tǒng)表里檢索。
mysqlmysql select * from information_schema.resource_groups;+---------------------+---------------------+------------------------+----------+-----------------+| RESOURCE_GROUP_NAME | RESOURCE_GROUP_TYPE | RESOURCE_GROUP_ENABLED | VCPU_IDS | THREAD_PRIORITY |+---------------------+---------------------+------------------------+----------+-----------------+| USR_default ? ? ? ? | USER ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ? ? ?1 | 0-3 ? ? ?| ? ? ? ? ? ? ? 0 || SYS_default ? ? ? ? | SYSTEM ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ? ? ?1 | 0-3 ? ? ?| ? ? ? ? ? ? ? 0 || user_ytt ? ? ? ? ? ?| USER ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ? ? ?1 | 0-1 ? ? ?| ? ? ? ? ? ? ?19 |+---------------------+---------------------+------------------------+----------+-----------------+3 rows in set (0.00 sec)
我們來給語句select guid from t1 group by left(guid,8) order by rand() 賦予RG user_ytt。
mysql show processlist;+-----+-----------------+-----------+------+---------+-------+------------------------+-----------------------------------------------------------+| Id ?| User ? ? ? ? ? ?| Host ? ? ?| db ? | Command | Time ?| State ? ? ? ? ? ? ? ? ?| Info ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|+-----+-----------------+-----------+------+---------+-------+------------------------+-----------------------------------------------------------+| ? 4 | event_scheduler | localhost | NULL | Daemon ?| 10179 | Waiting on empty queue | NULL ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|| 240 | root ? ? ? ? ? ?| localhost | ytt ?| Query ? | ? 101 | Creating sort index ? ?| select guid from t1 group by left(guid,8) order by rand() || 245 | root ? ? ? ? ? ?| localhost | ytt ?| Query ? | ? ? 0 | starting ? ? ? ? ? ? ? | show processlist ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|+-----+-----------------+-----------+------+---------+-------+------------------------+-----------------------------------------------------------+3 rows in set (0.00 sec)
找到連接240對(duì)應(yīng)的thread_id。
mysqlmysql select thread_id from performance_schema.threads where processlist_id = 240;+-----------+| thread_id |+-----------+| ? ? ? 278 |+-----------+1 row in set (0.00 sec)
給這個(gè)線程278賦予RG user_ytt。沒報(bào)錯(cuò)就算成功了。
mysqlmysql set resource group user_ytt for 278;Query OK, 0 rows affected (0.00 sec)
當(dāng)然這個(gè)是在運(yùn)維層面來做的,我們也可以在開發(fā)層面結(jié)合 MYSQL HINT 來單獨(dú)給這個(gè)語句賦予RG。比如:
mysqlmysql select /*+ resource_group(user_ytt) */guid from t1 group by left(guid,8) order by rand()....8388602 rows in set (4 min 46.09 sec)
RG的限制:
Linux 平臺(tái)上需要開啟 CAPSYSNICE 特性。比如我機(jī)器上用systemd 給mysql 服務(wù)加上
systemctl edit mysql@80 [Service]AmbientCapabilities=CAP_SYS_NICE
mysql 線程池開啟后RG失效。
freebsd,solaris 平臺(tái)thread_priority 失效。
目前只能綁定CPU,不能綁定其他資源。
網(wǎng)頁(yè)名稱:mysql線程池怎么樣 mysql 線程數(shù)與cpu的關(guān)系
標(biāo)題路徑:http://www.chinadenli.net/article44/dddchee.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供小程序開發(fā)、網(wǎng)站改版、動(dòng)態(tài)網(wǎng)站、網(wǎng)站排名、電子商務(wù)、網(wǎng)站內(nèi)鏈
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)