mysql併發執行緒控制之thread pool和優先佇列
之前根據官方資料整理了一篇筆記,現在讀起來感覺就是在湊數,在網上看到高人的一篇帖子,現在重新整理記錄一下。
Maria引入thread pool,percona在此基礎上引入優先佇列,依次對這兩種特性進行描述。
1 maria實現細節
1、整個連線池內部被分成N個小的group,N預設為cpu的個數,可以透過引數thread_pool_size設定,group之間透過round robin的方式分配連線,group內透過競爭方式處理連線,一個worker執行緒只屬於一個group;
2、每個group有一個動態的listener,worker執行緒在迴圈取event時,發現佇列為空時會充當listener透過epoll的方式監聽資料,並將監聽到的event放到group中的佇列;
注:每個group都建立一個epollfd,而空閒連線的socket都加入其中被監聽,只有當event queue為空時才會有worker呼叫epoll_wait;
3、延時建立執行緒,group中的活動執行緒數為0或者group被阻塞時,worker執行緒會被建立,worker執行緒被建立的時間間隔會隨著group內已有的執行緒數目的增加而變大;
4、worker執行緒,數目動態變化,這也是相對於5.1版本的一個改進,併發較大時會建立更多的worker執行緒,當從佇列中取不到event時work執行緒將休眠,超過thread_pool_idle_timeout後結束生命;
5、timer執行緒,它會每隔一段時間做兩件事情:
1)檢查每個group是否被阻塞,判定條件是:group中的佇列中有event,但是自上次timer檢查到現在還沒有worker執行緒從中取出event並處理;
2)kill超時連線並做一些清理工作;
connection生命週期
1 客戶端發起連線,acceptor執行緒呼叫add_connection scheduler callback接收;
每個worker都可充當acceptor,將認證和執行緒初始化offload到worker是為了避免可能的single acceptor阻塞;
登入完畢後,threadpool為connection socket開啟非同步讀;
2 客戶端呼叫query,socket發出訊號,由worker負責處理;
呼叫介面
static scheduler_functions tp_scheduler_functions=
{
...
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
};
以上函式呼叫時都會傳入THD
tp_init
執行緒池初始化工作,對每個group初始化pollfd,啟動timer執行緒
tp_add_connection
根據thd的thread_id採用round robin方式將其分配到一個group中,將thd封裝到connection_t的一個結構體中,放到group中的佇列中,入隊時會判斷group中的活動worker執行緒數(thread_group->active_thread_count)是否大於0,否則將喚醒或者建立一個worker執行緒(wake_or_create_thread)
Worker執行緒工作邏輯
迴圈從group佇列中get_event並呼叫handler_event
for(;;)
{
connection = get_event(&this_thread, thread_group, &ts);
if (!connection)
break;
this_thread.event_count++;
handle_event(connection);
}
get_event
從佇列中取event,取不到則充當listener透過epoll從網路監聽事件,如果最終還是沒能取到event,休眠一段時間,直到被喚醒或者超時退出;
handle_event:
1)連線驗證;2)處理連線請求直到連線空閒,為空閒連線設定一個超時期限,之後將連線的socket fd繫結到group中的epollfd
timer_thread執行邏輯
做了兩件事情:1)檢查是否有group被阻塞,阻塞將呼叫wake_or_create_thread;2)檢查是否有連線超時,超時將呼叫tp_post_kill_notification
for (;;)
{
/* Check stalls in thread groups */
for(i=0; i< array_elements(all_groups);i++)
{
if(all_groups[i].connection_count)
check_stall(&all_groups[i]);
}
/* Check if any client exceeded wait_timeout */
if (timer->next_timeout_check <= timer->current_microtime)
timeout_check(timer);
}
wake_or_create_thread
喚醒或者建立執行緒,被三處呼叫:
1)connection加入到queue中時,group中的佇列中沒有活動的worker執行緒:
tp_add_connection
|->queue_put
|->wake_or_create_thread
2)timer執行緒檢查是否有group阻塞,滿足兩個條件中的任一個即被呼叫:group中沒有listener並且佇列為空;group被阻塞
timer_thread
|->check_stall
|->wake_or_create_thread
3)事務被阻塞(如:鎖等待)透過MYSQL_CALLBACK介面呼叫,等待之前會確定group中有活動的worker執行緒,否則喚醒或者建立worker執行緒
tp_wait_begin
|->wait_begin
|->|->wake_or_create_thread
2 優先佇列
MariaDB版本有缺陷,為了發揮執行緒池的優勢,需要儘量控制執行緒池中執行緒數目,否則會退化成one-thread-per-connection,而如果嚴格控制執行緒池中執行緒資料,可能會出現排程上的死鎖。
percona在移植MariaDB threadpool的實現後進一步最佳化了執行緒池效能,透過引入優先佇列很好解決了這個問題。
工作原理
1 建立多個group(預設等同於cpu core數量),每個group可有多個worker;
2 執行緒根據connection id被分配到group(生命週期內不變),worker以sql為單位處理,保證每個連線都能及時得到響應;
3 每個group有兩個任務佇列,優先佇列:存放已開啟事務的sql,保證事務優先被處理完(儘早釋放鎖);優先佇列為空時才處理普通佇列;
可避免排程上的死鎖:(A和B被分到不同的group中,A事務已經開啟,並且獲得了鎖,可能無法立即得到排程執行,B事務依賴A事務釋放鎖資源,但是先於A得到排程);
4 額外建立一個timer執行緒,定期檢查groups,若發現woker異常則及時喚醒(堵塞/超時/worker執行緒數目不夠);
5 若group任務佇列為空(客戶端連線卻不為空),為空閒連線設定一個超時期限,之後將連線的socket fd繫結到group中的epollfd,執行緒則呼叫epoll_wait()批次取任務;
引數
root@(none) 05:33:27>show global variables like '%thread_pool%';
+-------------------------------+--------------+
| Variable_name | Value |
+-------------------------------+--------------+
| thread_pool_high_prio_mode | transactions |
| thread_pool_high_prio_tickets | 4294967295 |
| thread_pool_idle_timeout | 60 |
| thread_pool_max_threads | 100000 |
| thread_pool_oversubscribe | 3 |
| thread_pool_size | 24 |
| thread_pool_stall_limit | 500 |
+-------------------------------+--------------+
7 rows in set (0.00 sec)
thread_pool_high_prio_mode
有三個取值:transactions / statements / none
transactions(default): 使用優先佇列和普通佇列,對於事務已經開啟的statement,放到優先佇列中,否則放到普通佇列中
statements:只使用優先佇列
none: 只是用普通佇列,本質上和statements相同,都是隻是用一個佇列
thread_pool_high_prio_tickets
取值0~4294967295,當開啟了優先佇列模式後(thread_pool_high_prio_mode=transactions),每個連線最多允許thread_pool_high_prio_tickets次被放到優先佇列中,之後放到普通佇列中,預設為4294967295
thread_pool_idle_timeout
worker執行緒最大空閒時間,單位為秒,超過限制後會退出,預設60
thread_pool_max_threads
threadpool中最大執行緒數目,所有group中worker執行緒總數超過該限制後不能繼續建立更多執行緒,預設100000
thread_pool_oversubscribe
一個group中執行緒數過載限制,當一個group中執行緒數超過次限制後,繼續建立worker執行緒會被延遲,預設3
thread_pool_size
threadpool中group數量,預設為cpu核心數,server啟動時自動計算
thread_pool_stall_limit
timer執行緒檢測間隔,單位為毫秒,預設500ms
thread_pool_stall_limit 是後臺timer執行緒檢測任務是否堵塞的時間間隔,在併發壓力較大時,該引數設定過大可能會造成timer執行緒無法及時喚醒/建立worker執行緒
測試結果
詳情可見
參考資料
http://blog.chinaunix.net/uid-28364803-id-3431242.html
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/15480802/viewspace-1452252/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- mysql併發執行緒控制之控制thread_running數量MySql執行緒thread
- shell佇列實現執行緒併發控制(轉)佇列執行緒
- mysql併發執行緒控制之innodb_thread_concurrency的改進MySql執行緒thread
- iOS 多執行緒--GCD 序列佇列、併發佇列以及同步執行、非同步執行iOS執行緒GC佇列非同步
- MySQL多執行緒併發調優MySql執行緒
- Python3 執行緒優先順序佇列( Queue)Python執行緒佇列
- iOS併發機制(三) —— NSOperation實現多併發之建立佇列和開啟執行緒iOS佇列執行緒
- 堆和優先佇列佇列
- 深度理解GCD執行緒死鎖,佇列,同步和非同步,序列和併發GC執行緒佇列非同步
- 優先佇列和堆排序佇列排序
- Java多執行緒/併發07、Thread.Join()讓呼叫執行緒等待子執行緒Java執行緒thread
- Java阻塞佇列執行緒集控制的實現Java佇列執行緒
- Java之執行緒的優先順序Java執行緒
- Java併發程式設計:4種執行緒池和緩衝佇列BlockingQueueJava程式設計執行緒佇列BloC
- 併發與多執行緒之執行緒安全篇執行緒
- RMQ——支援合併和優先順序的訊息佇列MQ佇列
- 主佇列&主執行緒佇列執行緒
- 20170526-27關於GCD控制執行緒併發數,多執行緒併發數控制GC執行緒
- 合併果子(優先佇列 +或者+哈夫曼)佇列
- PHP優先佇列PHP佇列
- 堆--優先佇列佇列
- 優先佇列 (轉)佇列
- golang 限流器,控制併發,執行緒池Golang執行緒
- 五、併發控制(1):執行緒的互斥執行緒
- MySQL 配置InnoDB的併發執行緒MySql執行緒
- 執行緒控制之休眠執行緒執行緒
- Java 多執行緒基礎(十一)執行緒優先順序和守護執行緒Java執行緒
- java多執行緒:執行緒池原理、阻塞佇列Java執行緒佇列
- java-執行緒池佇列飽和策略Java執行緒佇列
- 多執行緒-執行緒控制之休眠執行緒執行緒
- 多執行緒-執行緒控制之加入執行緒執行緒
- 多執行緒-執行緒控制之禮讓執行緒執行緒
- Java 併發:執行緒、執行緒池和執行器全面教程Java執行緒
- Java 併發和多執行緒(一) Java併發性和多執行緒介紹[轉]Java執行緒
- Thread 併發執行例項thread
- Thread執行緒控制之sleep、join、setDaemon方法的用處thread執行緒
- 併發程式設計之:執行緒程式設計執行緒
- 多執行緒-執行緒排程及獲取和設定執行緒優先順序執行緒