本文由 Jilen 發表在 ScalaCool 團隊博客。mysql
Mysql Async 是一個 Scala 編寫的,基於 Netty 實現的非阻塞異步數據庫驅動。在本系列文章中咱們將逐步分析:git
項目官網設計目標以下github
Future
能夠看出做者是但願經過異步非阻塞能讓驅動更快(注意此處咱們不討論是真異步或者僞異步)。
接下來本文將具體分析與傳統的 mysql-connector/j
相比到底是不是更快,快在哪裏。sql
synchronized
和 Lock
mysql-connector/j
使用的仍是 Blocking IO ,這要求處理請求時必需有足夠多的線程,不然吞吐量將受很大限制。數據庫
例如一樣基於 Blocking IO 的 Tomcat7
默認就配置了 200 線程。編程
項目還提供一個鏈接池,採用分區設計,一個 PartitionedAsyncObjectPool
包含多個 SingleThreadedAsyncObjectPool
。網絡
流程十分簡單,根據線程的 id 選擇 SingleThreadedAsyncObjectPool
,而後從中獲取數據庫連接。不存在阻塞的可能併發
顧名思義,這是一個單線程的對象池。當請求獲取連接時,若是有多餘連接則直接返回,若是沒有則加入隊列,等待有連接經過 giveBack
方法釋放時返回給隊列裏的某個請求。
這裏用了 Scala 的 Future
和 Promise
實現,也不存在阻塞的狀況。框架
分析源代碼後發現此處使用只有一個線程的 ThreadPoolExecutor
來確保同一時間只有一個線程請求連接。異步
// Worker.scala
def action(f: => Unit) {
this.executionContext.execute(new Runnable {
def run() {
...
}
})
}複製代碼
上述代碼中this.executionContext.execute
最終會執行 TreadPoolExecutor.execute
而 TreadPoolExecutor.execute
並非徹底非阻塞的。
這帶來了一個問題:當多個線程同時要獲取連接時,只有一個線程能夠得到連接,其餘線程所有處於 blocked
狀態。
因爲是分區設計,而且 Play 這樣的全異步框架主線程數默認很是少,因此這個問題在某些場合下並不嚴重。
HikariCP 也許是目前優化得最好 JDBC 鏈接池。
該項目 Wiki 中的幾篇文章也值得一看。
咱們沒法從理論上直接得出何者性能更優的答案,後續將經過具體測試來估計何者更優。
爲了驗證上述觀點,我進行了簡單的性能測試,主要測試了簡單查詢和事務兩個方面。
SELECT 1複製代碼
update user set remain = remain + ? where id = ?
update user set remain = remain - ? where id = ?複製代碼
Mysql Async
有微弱的優點。hikaricp + mysql-connector/j
方案也許能夠提高性能,但這套方案的問題是你永遠不知道多少線程和連接數纔是合適的。下表是結合上述測試和定性分析得出的結果
項目 | MysqlAsync | HikariCP + mysql-connector/j |
---|---|---|
編程模型 | 異步 | 同步 |
網絡IO | NIO | BIO |
連接池 | 異步實現 | 同步實現 |
過載防禦 | 經過調節隊列長度實現 | 須要額外實現 (例如指定線程池任務隊列長度) |
可伸縮性 | 只須要設置合理鏈接數(例如幾十個) | 須要測試最佳線程數和連接數 |
線程數 | 少 | 多 |
總得來講 MysqlAsync 經過減小了線程數確實達到了如下效果