天天看點

MySQL核心月報 2014.12-MySQL· 性能優化·thread pool 原理分析

<b>大連接配接問題</b>

現有mysql 處理用戶端連接配接的方式會觸發mysql 建立一個線程來處理新的連接配接,建立的線程會處理該連接配接所發送的所有 sql 請求,即 one-thread-per-connection 的方式,其建立連接配接的堆棧為:

線程建立後,處理請求的堆棧如下:

0 mysql_execute_command

1 0x0000000000936f40 in mysql_parse

2 0x0000000000920664 in dispatch_command

3 0x000000000091e951 in do_command

4 0x00000000008c2cd4 in do_handle_one_connection

5 0x00000000008c2442 in handle_one_connection

6 0x0000003562e07851 in start_thread () from /lib64/libpthread.so.0

7 0x0000003562ae767d in clone () from /lib64/libc.so.6

<b>優點及存在的問題</b>

在連接配接數較小的情況下可以很快的響應用戶端的請求,但當連接配接數非常大時會建立很多線程,這樣會引起以下問題:

1. 過多線程之間的切換會加重系統的負載,造成系統資源緊張且響應不及時;

2. 頻繁的進行線程的建立及銷毀以及線程間同時無序的竟争系統資源加重了系統的負載。

thread_pool正是為了解決以上問題而産生的;

<b>什麼是thread_pool</b>

thread_pool(線程池),是指mysql 建立若幹工作線程來共同處理所有連接配接的使用者請求,使用者的請求的方式不再是 ‘one thread per connection’,而是多個線程共同接收并處理多個連接配接的請求,在資料庫的底層處理方面(mysql_execute_command),單線程的處理方式和線程池的處理方式是一緻的。

<b>thread_pool 的工作原理</b>

啟動 thread_pool 的mysql 會建立thread_pool_size 個thread group , 一個timer thread, 每個thread group 最多擁有thread_pool_oversubscribe個活動線程,一個listener線程,listener線程負責監聽配置設定到thread group中的連接配接,并将監聽到的事件放入到一個queue中,worker線程從queue中取出連接配接的事件并執行具體的操作,執行的過程和one thread per connection 相同。timer threaad 則是為了監聽各個threadgroup的運作情況,并根據是否陰塞來建立新的worker線程。

thread_pool 建立連接配接的堆棧如下:

thread group中的 worker 處理請求的堆棧如下:

4 0x0000000000a78533 in threadpool_process_request

5 0x000000000066a10b in handle_event

6 0x000000000066a436 in worker_main

7 0x0000003562e07851 in start_thread ()

8 0x0000003562ae767d in clone ()

其中worker_main函數是woker線程的主函數,調用mysql本身的do_command 進行消息解析及處理,和one_thread_per_connection 是一樣的邏輯; thread_pool 自行控制工作的線程個數,進而實作線程的管理。

thread_pool中線程的建立:

1. listener線程将監聽事件放入mysql放入queue中時,如果發現目前thread group中的活躍線程數active_thread_count為零,則建立新的worker 線程;

2. 正在執行的線程阻塞時,如果發現目前thread group中的活躍線程數active_thread_count為零,則建立新的worker 線程;

3. timer線程在檢測時發現沒有listener線程且自上次檢測以來沒有新的請求時會建立新的worker線程,其中檢測的時間受參數threadpool_stall_limit控制;

4. timer線程在檢測時發現沒有執行過新的請求且執行隊列queue 不為空時會建立新的worker線程;

worker線程的僞碼如下:

thread_pool中線程的銷毀:

當從隊列queue中取出的connection為空時,則此線程銷毀,取connection所等待的時間受參數thread_pool_idle_timeout的控制; 綜上,thread_pool通過線程的建立及銷毀來自動處理worker的線程個數,在負載較高時,建立的線程數目較高,負載較低時,會銷毀多餘的worker線程,進而降低連接配接個數帶來的影響的同時,提升穩定性及性能。同時,threadpool中引入了timer 線程,主要做兩個事情。

1. 定期檢查每個thread_group是否阻塞,如果阻塞,則進行喚醒或建立線程的工作;

2. 檢查每個thread_group中的連接配接是否逾時,如果逾時則關掉連接配接并釋放相應的資源;

threadpool在使用中存在的問題:

1. 由于threadpool嚴格控制活躍線程數的限制,如果同時有多個大查詢同時配置設定到了同一個thread group,則會造成此group中的請求過慢,rt 升高,最典型的就是多個binlog dump 線程同時配置設定到了同一個group内;

2. 開啟了事務模式時,非事務模式的請求會放入低優先級隊列,是以可能在較長時間内得不到有效處理,極端情況下,會導緻執行個體hang 住,例如某個連接配接執行了 flush tables with read lock ,并且此連接配接的後續請求都會放入低優先級,那麼有可能會造成實列hang住;

3. 較小并發下,threadpool 性能退化的問題;

繼續閱讀