相信不少看太小蜜蜂(BeeCP)鏈接池的性能對比圖的網友都會以爲驚訝不已,這比被號稱史上最快的光鏈接池還快啊,簡直難以想象!想知道它性能強悍的緣由嗎?今天就爲你們解開這個謎團。git
鏈接借用
故事仍是要從鏈接池自己提及,鏈接池技術是一門古老的IT技術,其本質並不複雜,其工做原理相似圖書館,只不過它借出/回收的是鏈接對象,在鏈接池類內部通常至少有兩條列表,第一條存放鏈接對象(相似書架),第二條存放等待者線程(或隱式), 若是鏈接都被借光了,借用者只能排隊等待其餘借用者歸還,等待過程當中,某些等待者可能會由於時間達到最大容許時刻點,則會退出等待,這種離開現象叫等待超時。github
傳遞與隊列
借用在使用完畢後將被關閉(實際關閉只是代理對象而已),背後自動觸發鏈接池對鏈接的回收,若是在池中存在等待者,那麼鏈接池將回收過來的鏈接傳遞給等待者,這是一件頗有意思的事情,各鏈接池的性能的差別,很大一部分是由傳遞的速度致使的(誰的傳遞效率高,誰的性能更強),傳遞的方式一般是藉助於同步隊列的管道推拉方式完成的,好比Tomcat-JDBC使用的是閒/忙兩條隊列,光鏈接池採用的是同步隊列(SynchronousQueue)代碼相似:數組
歸還線程A:queue.offer(object); 等待線程B:queue.poll(time);
對於線程 B在有效時間範圍內進行等待,不管採用LinkedBlockingQueue仍是採SynchronousQueue,背後直接或間接使用了等待鏈技術,好比SynchronousQueue就比較直白式,自定義了節點鏈,好比LinkedBlockingQueue則是經過Lock對象來完成的,其實Lock對象的本質就是等待鏈,有興趣的同窗能夠翻看一下源碼,AbstractQueuedSynchronizer對象是Lock的源頭,它內部就包含一條CAS節點鏈,看類上的介紹就清楚了,不用細看代碼,通常同窗只是使用而已,又不搞JDK代碼開發, 沒有必要研究太深。緩存
傳遞效率分析
對鏈接池而言,傳遞的效率直接決定其性能的高低,若是存在大量的併發的時候,其實使用Offer/Poll的方式,性能並不會很理想,以SynchronousQueue爲例說明:A爲歸還線程,B爲等待者線程,C爲剛進入的借用者線程,A在歸還的時候,首先將鏈接設置爲閒置狀態,並經過隊列傳遞給B, 此時C從鏈接數組中( 或CopyOnWriteArrayList)搜索的時也發現這個剛釋放的鏈接,所以B與C同時經過才CAS的方式搶佔這個鏈接,假如B搶佔失敗,又未超時,怎麼辦呢?繼續Poll(本質上是再次加入等待鏈)有可能存在反覆出入等待鏈的狀況,你們能夠想一想,假如很多線程發生這種狀況,那麼勢必形成性能損耗,甚至出現早加入等待者比後來加入的等待者須要更多時間纔得到鏈接,爲啥呢?每次失敗後,都是從新加入等待鏈的末端,從而違背了FIFO原則,理想方式是:我先加入等待,那麼我應該比後等待者先得到對象。 (這就是某池在併發比較多的狀況,部分請求出現時間夸特別度大的根本緣由)安全
小蜜蜂池如何傳遞
小蜜蜂鏈接池採用併發隊(ConcurrentLinkedQueue)隊列完成等待/傳遞動做,隊列中的等待者在未得到有效的鏈接前,是不會離開隊列的;回收後的鏈接老是從隊列首位置進行傳遞,所以保正FIFO公平性原則( 按隊列排隊次序得到傳遞的機會 ),這個處理方式大大減小不要的隊列中移動,從而大大提高傳遞(Transfer)的效率,這個方式是鏈接池領域中一次技術創新,是小蜜蜂鏈接池與其餘鏈接池區分開來的重要標誌,我將它稱之爲小蜜蜂隊列技術。併發
小蜜蜂鏈接池在處理Transfer的方式,採用兩種業務模式:公平與競爭; 在公平模式下,釋放的鏈接在不改變狀態的前提下(狀態爲使用 中:USING)進行傳遞,在這個模式下,上述的C線程經過cas的方式是沒法持有的,從而保證B能順利得到此鏈接;在競爭模式下,B與C搶佔這個鏈接,若是B成功取得後,再檢測是有一個有效的鏈接後,纔會離開等待隊列;若是B搶佔失敗後,在未超時間的狀況下,那麼B是不會離開隊列,排列B的後面有B2,B3.....性能
除了擁有這個創新式技術,小蜜蜂鏈接池還有其餘一些亮點,下面一一羅列。優化
1:合理的線程控制url
學過計算機的同窗都知道,CPU是採用分時執行運行時線程的,能夠想象一下若是有大量併發的線程同時運行,那麼CPU須要再這些線程之間切換運行,勢必會形成較大的損耗,小蜜蜂鏈接池使用信號量(Semaphore:可形象化比喻爲一個具備多通道的門)的方式控制併發借用線程數,從而減小CPU一些沒必要要的切換。.net
以4核CPU爲例,鏈接的最大個數爲8個,將併發控制爲4,保證同一時刻4個線程能夠進入getConnection方法的信號量內部,指望的理想狀態是:4個鏈接處於待借,4個鏈接處於使用中,使每一個核覆蓋2個線程,這樣既不給CPU形成很大壓力,又能充分利用CPU
2:合理利用隊列與緩存
A: 小蜜蜂鏈接池中,在兩處可能發生等待現象:第一處是等待信號量的許可(信號量背後就是等待鏈),第二處等待是等待傳遞(Transfer),處理的方式是:大隊列等待,小隊列交換,意思是說:在大量併發的狀況,信號量的等待鏈中持有更多的更多等待線程,讓小規模的併發隊列來完成第二處等待和傳遞。
B: 小蜜蜂鏈接池中採用單鏈接緩存,並在到達信號量位置以前,自行經過CAS的方式嘗試搶佔曾經使用過的鏈接(若是多緩存,還不如直接從列表獲取呢)
3: 精準控制超時
借用線程進入借用方法(getConnection)後,預先計算超時時刻點(既借用者線程在遲內活動的時刻點不能超過該點),超時時間默認是8秒,意爲:8秒之內未取得鏈接,借用者線程自動離開池方法,這個時間是覆蓋池內的兩處可能出現等待的時間之和。
4:鏈接的安全保證
A: JDBC物理隊對象經過代理分裝,不容許調用者直接獲取
B: 鏈接在使用完畢後,重置處理,避免髒鏈接歸還到池中(若是出現,關閉老的,則補充一個新)
C: 鏈接有效性判斷檢查(存活性,閒置超時,持有不用超時)
D: 安全性關閉(若是Close方法被多個併發線程關閉,只能有一個關閉成功)
E: 三連級檢查(Result檢查本身的close,還要檢查Statement是否關閉,再上一級Connection是否關閉)確保對象的安全性
5:代碼極致優化
A: PSCache緩存優化(包括hashKey的計算)
B: 去掉一些get/set方法,直接使用對象的屬性值
C: 一些CasUpdator不是放對象內部,減小方法調用次數
總之來講,小蜜蜂鏈接池是一款優秀的開元做品,但願推薦給更多公司和我的使用,同時歡迎各位網友提問,或建議,或質疑. 項目工程請訪問: [https://github.com/Chris2018998/BeeCP]