Mysql線程池系列一(Thread pool FAQ)html
首先介紹什麼是mysql thread pool,幹什麼用的?
使用線程池主要能夠達到如下兩個目的:
一、在大併發的時候,性能不會由於過載而迅速降低。
二、減小性能抖動mysql
thread pool的工做原理?
線程池使用分而治之的方法來限制和平衡併發性。與默認的thread_handling不一樣,線程池將鏈接和線程劃分開,因此鏈接數量和執行語句的線程數再也不是固定的關係,線程池能夠經過
配置線程組來管理鏈接,而後再根據每一個語句的關鍵字來肯定是優先執行或者排隊執行。linux
mysql thread pool和client端的connection pool的不一樣之處?
client段的connection pool:鏈接池主要用來管理客戶端的鏈接,避免重複的鏈接/斷開操做,而是將空閒的鏈接緩存起來,能夠複用。從而減小了鏈接mysql server/斷開mysql server的開銷與成本,從而提高性能。
可是mysql的connection pool不能獲取mysql server的查詢處理能力以及當前的負載狀況。
thread pool:線程池的操做是在mysql server端,而且設計就是用來管理當前併發的鏈接和查詢.算法
thread pool到底可以提高多少性能?
根據Oracle Mysql官方的性能測試
在併發達到128個鏈接之後.沒有線程池的Mysql性能會迅速下降。使用線程池之後,性能不會出現波動,會一直保持在較好的狀態運行。
在讀寫模式下,128個鏈接之後,有線程池的Mysql比沒有線程池的Mysql性能高出60倍。
在只讀模式下,512個鏈接之後,有線程池的Mysql比沒有線程池的Mysql性能高出18倍。sql
何時能夠考慮使用thread_pool?
* show global status like ‘%threads_running%’;的值是mysql server當前併發執行語句的數量軌跡,若是這個值一直保持在40左右的區間,那麼能夠考慮使用thread pool。
*若是你使用了innodb_thread_concurrency參數來控制併發的事物量,那麼使用線程池將會得到更好的效果。
*若是你的工做是有不少短鏈接組成的,那麼使用線程池是有益的。數據庫
說一下oracle mysql thread pool插件的限制:
一、Oracle Mysql enterprise 6.10版本添加的,也就是說小於這個版本的企業版不支持,目前全部的oracle mysql community版本也不支持。
二、若是是windows的系統,須要是vista或者之後的版本,若是是linux,須要2.6.9之後的內核。windows
Mysql線程池系列二(Oracle Mysql Thread pool的安裝和原理)緩存
thread pool的組件和安裝
thread pool是以插件的方式存在的,安裝thread pool插件之後,會增長一些information_schema表和相關參數變量。
information_schema表包含:
TP_THREAD_STATE
TP_THREAD_GROUP_STATE
TP_THREAD_GROUP_STATS服務器
新增長的參數變量:
thread_handling增長了一個值,loaded-dynamically,當成功加載thread pool插件的時候就是這個值了。
thread_pool_algorithm:
thread_pool_high_priority_connection:
thread_pool_prio_kickup_timer:
thread_pool_max_unused_threads:
thread_pool_size:
thread_pool_stall_limit:
若是這些值設置的不正確,那麼啓動mysql的時候插件會初始化失敗,插件將不能加載。
這些變量的具體設置方法,會在接下來的優化章節裏面詳細的介紹。多線程
thread pool插件的對象庫必須放在plugin_dir變量對應的目錄裏。爲了使thread pool生效,能夠在啓動mysql的時候使用–plugin-load選項。或者修改my.cnf文件.在[mysqld]區段中添加以下信息
[mysqld]
plugin-load=thread_pool.so
thread pool的原理
thread pool包含數個thread groups,每一個thread group管理一組客戶端鏈接。當鏈接創建之後,thread pool以輪詢的方式分配他們到thread group.
thread group的數量是經過thread_pool_size配置獲得的,默認是16個,最大64個,最小1個。
每一個thread group最大能夠有4096個線程。
線程池把鏈接和線程分開了,因此鏈接和線程不是固定對應的,線程執行從connections收到的語句,這和默認的thread_handling模式不一樣。
thread_handling參數
原來的版本里面有一個thread_handling參數,能夠設置thread的工做模式,有兩個值,
一個是no-threads,指任意時刻最多隻有一個鏈接能夠鏈接到mysql server,通常用於調試。另一個是one-thread-per-connection,是指針對每一個鏈接建立一個線程來處理這個鏈接的全部請求,直到鏈接斷開,線程結束.這也是thread_handling的默認值。
因而可知,默認狀況下,多少鏈接就會產生多少個線程,併發越大,線程越多,線程之間的資源競爭越激烈,性能越低。
thread pool插件提供另外的一種thread_handling方法,用來有效的管理執行線程與大量客戶端鏈接,從而提升性能。
線程池解決的幾個問題:
*高併發的多線程棧致使CPU的緩存幾乎失效,線程池促進線程堆棧重用,減小CPU緩存量。
*太多的線程併發執行,上下文切換開銷很高,這對操做系統的任務調度是一個很大的挑戰,線程池能夠把mysql活躍的併發線程控制在一個適合mysql server運行的水平。
*太多的事務併發執行會增長資源爭用,在innodb引擎裏,會增長獲取central mutexes的時間,線程池能夠控制事務的併發量。
thread pool嘗試保證每一個thread group中的每一個thread儘可能執行更多的語句,可是有些時候容許更多的線程執行一些臨時的任務來提升性能。算法的工做方式以下:
*每一個trhead group有一個listener,這個listener負責監聽分配給thread group的statements,thread group有兩種執行方案,一是當即執行,一種是排隊執行。
*當即執行的條件是當前只收到一條statement,而且當前沒有statements在執行。
*排隊執行發生在不能當即執行的時候
*噹噹即執行發生的時候,是由listener線程執行的,也就是說listener在執行一些臨時的statements,若是當即執行的statement很快執行完成,那麼這個線程會變回listener線程,若是其餘狀況thread pool會考慮重新開啓一個listener線程來代替它,是否須要建立listener線程是由thread pool的後臺線程來監控和執行的。
當thread pool插件啓動之後,每一個thread group會建立一個listener線程,加上background線程,其餘線程根據是否須要而建立。
thread_pool_stall_limit系統變量的含義能夠理解爲完成一個statement須要的時間,默認認爲stalled的時間是60ms,最大能夠設置爲6s。配置這個參數可讓你平衡服務器的工做負載.這個值設置的越小線程啓動越快,更小的值能夠更好的避免死鎖,更大的值一般在不少長查詢的時候使用,爲了不啓動太多的線程。
thread pool的焦點在於限制併發的短查詢語句的數量,在一個語句執行時間沒有達到stall的時候,阻止其餘statements開始執行,若是一個statement執行超過了stall time,它將會繼續執行,可是不在阻止其餘statement開始執行。用這種方法,thread pool嘗試確保每一個thread group歷來沒有超過一個short-running statement,儘管會有多個long-running statement。讓長時間執行的語句阻止其餘語句的執行,這是不可取的,由於沒有限制等待的最長時間.例如,在一個replication的master,一個線程一直髮送binlog給slave。
一個statement由於I/O操做或者用戶級別的鎖被阻塞了,這個阻塞將會致使thread group無效,因此回調函數會通知thread pool確認,而且thread pool會立刻在這個thread group中啓動一個新的線程執行其餘的statement.當被阻塞的線程返回時,thrad pool容許立刻從新啓動。
這裏有兩種隊列(queue),一種是高優先級的隊列(high-priority queue),和一種低優先級的隊列(low-priority queue).事務中的第一個statement會被分配到低優先級的隊列,剩下的statement將會被分配到高優先級的隊列裏(前提是這個事務已經開始執行了),或者被分配到低優先級隊列。
隊列的分配受到thread_pool_high_priority_connection系統變量影響,這個參數的默認值是0,表示同時使用低優先級隊列和高優先級隊列,若是值設置爲1,全部queued statements都會被直接分配到高優先級的隊列。
對於非事務的存儲引擎的statements,或者是autocommit的存儲引擎,都會被放入低優先級的queue處理,由於每一個statement都是一個事務。所以,使用innodb和myisam混合引擎的數據庫,thread pool認爲innodb的優先級高於myisam的優先級,除非innodb開啓了autocommit。若是autocommit開啓,那麼全部的statements都屬於低優先級。
當thread group選擇一個queue中的statement執行的時候,它會優先在高優先級的queue中查找,而後纔在低優先級的queue中查找,若是找到tatement,他就會從queue中刪除這個statement,而後開始執行它。
若是一個statement在低優先級的queue中等待好久,它將被thread pool移動到高優先級的queue裏.等待的時間由thread_pool_prio_kickup_timer決定。
thread pool對活躍線程的重用,能夠更好的使用CPU caches.這個很小的調整對性能的提高卻有很大幫助。
thread group分配多個線程執行statement的狀況:
*一個線程開始執行一個statement,可是執行時間達到stalled之後,thread group容許其餘線程開始執行其餘statement,以前的線程繼續執行以前的statement。
*一個線程開始執行一個statement,可是線程被阻塞了,報告給thread pool之後,thread group容許其餘線程開始執行其餘statement。
*一個線程開始執行一個statement,可是線程被阻塞了,因爲阻塞不是發生在代碼層,因此沒有報告給thread pool。當阻塞時間達到stall之後,thread group容許其餘線程執行其餘statement。
線程的設計能夠針對不斷增長的鏈接具備擴展性,同時他的設計也能夠經過限制併發的thread來儘可能避免死鎖發生.可是要注意的是,阻塞的線程若是沒有報告thread pool,那麼thread pool就不會阻塞其餘線程的運行,
這種狀況可能會致使線程池死鎖。
*長時間運行的statments,不多的statements將使用全部的資源,這將致使服務器拒絕全部其餘的訪問。
*binary log dump線程讀取binlog,而後發送給slave,這是一種長時間運行的」statement」,他不會阻止其餘的statements運行.
*statement能夠被row級別、table級別的鎖阻塞,也能夠被sleep等其餘緣由的鎖阻塞,或者其餘被阻塞的沒有報告thread pool的thread阻塞了。
上面每種狀況,都是爲了防止死鎖,沒有快速執行完成的statement將被移動到stalled分類,因此thread group容許其餘statement開始執行。因爲這個設計,當線程在執行或者被阻塞的時間內,thread pool把這些線程
標記爲stalled類型,而後餘下的statement將會被執行,它沒有拒絕其餘statments的執行.
最大的線程數能夠達到max_connections和thread_pool_size的和,這種狀況只有在全部的鏈接都在同時執行,而且每一個thread group開啓一個listen線程來監聽新的statement。這種狀況很難發生,可是理論上存在。
Mysql線程池系列三(Oracle Mysql Thread pool調優)
首先明確調優的目的是提升TPS。
thread_pool_size:
是一個很是重要的參數,控制thread pool的性能,具體表現爲thread group的數量。只能在server啓動以前設置,咱們測試thread pool的結果以下:
*若是主存儲引擎是innodb,thread_pool_size設置在16至36之間,大多數狀況設置在24到36,咱們尚未發現什麼狀況須要設置超過36,也只有一些特殊的環境須要設置小於16.
使用DBT2或者sysbench作測試的時候,innodb引擎下一般設置爲36個,若是在一些寫入特別多的環境,這個值能夠設置的更小一些。
*若是主存儲引擎是myisam,thread_pool_size須要設置的更低,咱們推薦的值是4到8,更高的值可能會對性能有負面影響。
thread_pool_stall_limit:
這個參數對於處理阻塞和長時間執行的語句很重要。這個時間是從一個statement從開始執行到執行完成總花費的時間,若是超過設置值就被認定爲stalled,此時線程池也開始容許執行另外
一個statement。這個值的單位是10毫秒,默認值是6,也就是默認間隔是60ms,一個statement執行超過60ms,就被認爲是stalled。最大值是600,也就是6秒。通常這個值設置爲你99%的statement能夠執行完的時間。好比我慢查詢設置的
是0.1,那麼這裏就設置爲10。另外能夠經過
SELECT SUM(STALLED_QUERIES_EXECUTED) / SUM(QUERIES_EXECUTED) FROM information_schema.TP_THREAD_GROUP_STATS;來獲取stalled的比例,這個值儘可能的小,爲了不stall,能夠調高thread_pool_stall_limit的值。
thread_pool_prio_kickup_timer:
這個值影響低優先級的statements的queue。參數值的單位是毫秒,低優先級的statement須要等到多少毫秒才能被移動到高優先級的queue.默認是1000,也就是1秒,值的範圍是(0-4294967294)。
thread_pool_high_priority_connection:
這個參數主要決定新來的statements的執行優先級。默認值是0,表示同時使用low-prority queue和high-priority queue。若是設置爲1,全部的statement都會分配到high-priority queue。
thread_pool_max_unused_threads:
這個參數限制thread pool中sleeping thread的最大數量。從而限制sleeping thread對內存的使用。
若是參數的值爲0,也就是默認值,意味着對sleeping thread沒有限制.假設值爲N,當N大於0的時候,意味着1個consumer thread和N-1個 reserve thread。意思也就是說,當一個線程執行完一個statement,將要轉爲sleeping狀態的時候,這時sleeping狀態的
線程數量已經達到了容許的sleeping thread的最大數量,那麼這個線程將會退出。
關於consumer thread:sleeping thread由consumer thread和reserve thread組成,thread pool容許sleeping thread中有一個consumer thread,一個thread要轉變爲sleepling thread的時候,若是沒有consumer thread 存在,那麼
這個thread將轉變爲consumer thread.當一個sleeping thread要被喚醒的時候,若是存在consumer thread,那麼優先喚醒consumer thread,若是consumer thread不存在,那麼喚醒reserve thread。
thread_pool_algorithm:此參數決定thread pool使用那種算法.默認值是0,表示使用較低的併發算法,在大多數測試和生產環境下效果很好。另一個值是1,更加積極的增長併發數量的算法,有時候會比最佳線程數量性能更好,可是隨着鏈接的增長,性能會逐漸降低。因此這個參數主要用在實驗環境。