如何正確設定資料庫連線池的大小?我的天,原來之前都設定錯了!
文章翻譯整理自: https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing
歡迎關注個人微信公眾號: 小哈學Java, 文末分享阿里 P8 資深架構師吐血總結的 《Java 核心知識整理&面試.pdf》資源連結!!
個人網站: https://www.exception.site/essay/how-to-set-the-size-of-database-connection-pool
目錄
- 一、筆者前言
- 二、正菜開始
- 三、假設你的服務有1萬併發的訪問
- 四、為啥有這種效果?
- 五、其他應該考慮到的因素
- 六、連線數計算公式
- 七、結論:你需要的是一個小連線池,和一個等待連線的執行緒佇列
- 八、額外需要注意的點
一、筆者前言
基本上來說,大部分專案都需要跟資料庫做互動,那麼,資料庫連線池的大小設定成多大合適呢?
一些開發老鳥可能還會告訴你:沒關係,儘量設定的大些,比如設定成 200,這樣資料庫效能會高些,吞吐量也會大些!
你也許會點頭稱是,真的是這樣嗎?看完這篇文章,也許會顛覆你的認知哦!
二、正菜開始
可以很直接的說,關於資料庫連線池大小的設定,每個開發者都可能在一環節掉進坑裡,事實上呢,大部分程式設計師可能都會依靠自己的直覺去設定它的大小,設定成 100 ?思量許久後,自顧自想,應該差不多吧?
三、假設你的服務有1萬併發的訪問
不妨意淫一下,你手裡有個網站,併發壓力雖然還沒到 Facebook 那個級別,但是呢?也有個1萬上下的併發量!也就是說差不多2萬左右的 TPS。
那麼問題來了!這個網站的資料庫連線池應該設定成多大合適呢?
其實這個問法本身就是有問題的,我們需要反過來問,正確問法應該是:
“這個網站的資料庫連線池應該設定成多小合適呢?”
PS: 這裡有一個 Oracle 效能小組釋出的簡短視訊,連結地址為 http://www.dailymotion.com/video/x2s8uec
口述一下,視訊中對 Oracle 資料庫進行了壓力測試,模擬 9600 個併發執行緒來操作資料庫,每兩次資料庫操作之間 sleep 550ms,注意,視訊中剛開始設定的執行緒池大小為 2048。
讓我們來看看資料庫連線池的大小為 2048 效能測試結果的鬼樣子:
每個請求要在連線池佇列裡等待 33ms,獲得連線之後,執行SQL需要耗時77ms, CPU 消耗維持在 95% 左右;
接下來,我們將連線池的大小改小點,設定成 1024,其他測試引數不變,結果咋樣?
"這裡,獲取連線等待時長基本不變,但是 SQL 的執行耗時降低了!"
哎呦,有長進哦!
接下來,我們再設定小些,連線池的大小降低到 96,併發數等其他引數不變,看看結果如何:
每個請求在連線池佇列中的平均等待時間為 1ms, SQL 執行耗時為 2ms.
我去!什麼鬼?
我們沒調整任何東西,僅僅只是將資料庫連線池的大小降低了,這樣,就能把之前平均 100ms 響應時間縮短到了 3ms。吞吐量指數級上升啊!
你這也太溜了!
四、為啥有這種效果?
我們不妨想一下,為啥 Nginx 內部僅僅使用了 4 個執行緒,其效能就大大超越了 100 個程序的 Apache HTTPD 呢?追究其原因的話,回想一下電腦科學的基礎知識,答案其實非常明顯。
要知道,即使是單核 CPU 的計算機也能“同時”執行著數百個執行緒。但我們其實都知道,這只不過是作業系統快速切換時間片,跟我們玩的一個小把戲罷了。
一核 CPU同一時刻只能執行一個執行緒,然後作業系統切換上下文,CPU 核心快速排程,執行另一個執行緒的程式碼,不停反覆,給我們造成了所有程序同時執行假象。
其實,在一核 CPU 的機器上,順序執行A和B永遠比通過時間分片切換“同時”執行A和B要快,其中原因,學過作業系統這門課程的童鞋應該很清楚。一旦執行緒的數量超過了 CPU 核心的數量,再增加執行緒數系統就只會更慢,而不是更快,因為這裡涉及到上下文切換耗費的額外的效能。
說到這裡,你應該恍然大悟了 ……
五、其他應該考慮到的因素
上小節中說到了主要原因,但其實沒有這麼簡單,我們還需要考慮到一些其他的因素。
當我們在尋找資料庫的效能瓶頸時,大致可歸為三類:
- CPU
- 磁碟 IO
- 網路 IO
也許你會說,還有記憶體這一因素?記憶體的確是需要考慮的,但是比起磁碟IO和網路IO,稍顯微不足道,這裡就不加了。
假設我們不考慮磁碟 IO 和網路 IO,就很好定論了,在一個 8 核的伺服器上,資料庫連線數/執行緒數設定為 8 能夠提供最優的效能,如果再增加連線數,反而會因為上下文切換導致效能下降。
大家都知道,資料庫通常把資料儲存在磁碟上,而磁碟呢,通常是由一些旋轉著的金屬碟片和一個裝在步進馬達上的讀寫頭組成的。讀/寫頭同一時刻只能出現在一個位置,當它需要再次執行讀寫操作時,它必須“定址”到另外一個位置才能完成任務。所以呢?這裡就有了定址的耗時,此外還有旋轉耗時,讀寫頭需要等待磁碟碟片上的目標資料“旋轉到位”才能進行讀寫操作。使用快取當然是能夠提升效能的,但上述原理仍然適用。
在這段("I/O等待")時間內,執行緒是處於“阻塞”等待狀態,也就是說沒幹啥正事!此時作業系統可以將這個空閒的CPU 核心用於服務其他執行緒。
這裡我們可以總結一下,當你的執行緒處理的是 I/O 密集型業務時,便可以讓執行緒/連線數設定的比 CPU核心大一些,這樣就能夠在同樣的時間內,完成更多的工作,提升吞吐量。
那麼問題又來了?
大小設定成多少合適呢?
這要取決於磁碟,如果你使用的是 SSD 固態硬碟,它不需要定址,也不需要旋轉碟片。打住打住!!!你千萬可別理所當然的認為:“既然SSD速度更快,我們把執行緒數的大小設定的大些吧!!”
結論正好相反!無需定址和沒有旋迴耗時的確意味著更少的阻塞,所以更少的執行緒(更接近於CPU核心數)會發揮出更高的效能。只有當阻塞密集時,更多的執行緒數才能發揮出更好的效能。
上面我們已經說過了磁碟 IO, 接下來我們談談網路 IO!
網路 IO 其實也是非常相似的。通過乙太網介面讀寫資料時也會造成阻塞,10G頻寬會比1G頻寬的阻塞耗時少一些,而 1G 頻寬又會比 100M 頻寬的阻塞少一些。通常情況下,我們把網路 IO 放在第三順位來考慮,然而有些人會在效能計算中忽略網路 IO 帶來的影響。
上圖是 PostgreSQL 的基準效能測試資料,從圖中我們可以看到,TPS 在連線數達到 50 時開始變緩。回過頭來想下,在上面 Oracle 的效能測試視訊中,測試人員們將連線數從 2048 降到了 96,實際上 96 還是太高了,除非你的伺服器 CPU 核心數有 16 或 32。
六、連線數計算公式
下面公式由 PostgreSQL 提供,不過底層原理是不變的,它適用於市面上絕大部分資料庫產品。還有,你應該模擬預期的訪問量,並通過下面的公式先設定一個偏合理的值,然後在實際的測試中,通過微調,來尋找最合適的連線數大小。
連線數 = ((核心數 * 2) + 有效磁碟數)
核心數不應包含超執行緒(hyper thread),即使打開了超執行緒也是如此,如果熱點資料全被快取了,那麼有效磁碟數實際是0,隨著快取命中率的下降,有效磁碟數也逐漸趨近於實際的磁碟數。另外需要注意,這一公式作用於SSD 的效果如何,尚未明瞭。
好了,按照這個公式,如果說你的伺服器 CPU 是 4核 i7 的,連線池大小應該為 ((4 * 2) + 1) = 9
。
取個整, 我們就設定為 10 吧。你這個行不行啊?10 也太小了吧!
你要是覺得不太行的話,可以跑個性能測試看看,我們可以保證,它能輕鬆支撐 3000 使用者以 6000 TPS 的速率併發執行簡單查詢的場景。你還可以將連線池大小超過 10,那時,你會看到響應時長開始增加,TPS 開始下降。
七、結論:你需要的是一個小連線池,和一個等待連線的執行緒佇列
假設說你有 10000 個併發訪問,而你設定了連線池大小為 10000,你怕是石樂志哦。
改成 1000,太高?改成 100?還是太多了。
你僅僅需要一個大小為 10 資料庫連線池,然後讓剩下的業務執行緒都在佇列裡等待就可以了。
連線池中的連線數量大小應該設定成:資料庫能夠有效同時進行的查詢任務數(通常情況下來說不會高於 2*CPU核心數)。
你應該經常會看到一些使用者量不是很大的 web 應用中,為應付大約十來個的併發,卻將資料庫連線池設定成 100, 200 的情況。請不要過度配置您的資料庫連線池的大小。
八、額外需要注意的點
實際上,連線池的大小的設定還是要結合實際的業務場景來說事。
比如說,你的系統同時混合了長事務和短事務,這時,根據上面的公式來計算就很難辦了。正確的做法應該是建立兩個連線池,一個服務於長事務,一個服務於"實時"查詢,也就是短事務。
還有一種情況,比方說一個系統執行一個任務佇列,業務上要求同一時間內只允許執行一定數量的任務,這時,我們就應該讓併發任務數去適配連線池連線數,而不是連線數大小去適配併發任務數。
Ref
https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing
免費分享 | 面試&學習福利資源
最近在網上發現一個不錯的 PDF 資源《Java 核心知識&面試.pdf》分享給大家,不光是面試,學習,你都值得擁有!!!
獲取方式: 關注公眾號: 小哈學Java, 後臺回覆資源,既可免費無套路獲取資源連結,下面是目錄以及部分截圖:
重要的事情說兩遍,關注公眾號: 小哈學Java, 後臺回覆資源,既可免費無套路獲取資源連結 !!!
歡迎關注微信公眾號: 小哈學Java
相關推薦
如何正確設定資料庫連線池的大小?我的天,原來之前都設定錯了!
文章翻譯整理自: https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing 歡迎關注個人微信公眾號: 小哈學Java, 文末分享阿里 P8 資深架構師吐血總結的 《Java 核心知識整理&面試.pdf》資源連結!! 個人網站
為何要設定資料庫連線池的 超時等待時間
先說問題背景:程式有多執行緒呼叫,有大量的資料庫互動 遇到的問題:程式執行一段時間之後hung 住了,沒有異常丟擲,也不繼續執行 因為有多執行緒呼叫,因此無法快速準確的定位問題:是執行緒問題還是其他程式碼邏輯問題 後來發現是資料庫連線的問題:由於有段程式碼資料庫連線
你真的知道如何設定資料庫連線池的大小嗎
前段時間在一個老專案中經歷過一個問題:一個 Dubbo 服務,啟動的時候慢的要死,後來看日誌查原因整個過程一直在初始化資料庫連線。一看資料庫連線引數,連線池大小:1024。 很多入行晚的同學沒有經歷過手寫 JDBC 連線的日子。那個時候沒有資料庫連線池的概念,都是原生程式碼一頓搞,後來有了 iBATIS 之後
資料庫預設連線數 遠遠 大於 資料庫連線池的 連結數,為什麼還要使用資料庫連線池
1. 資料庫的讀寫能力與計算機硬體相關, 所以連結太多,資料庫也不能一下子處理。連線數超過資料庫最大連線數,連結就會等待,超時就會報錯。 2. 建立連結很浪費資源。資料庫連線池保持現有連線後不會再建立連線池,而是複用這些連結。 3. 如果併發超過連線池連線數,則連結排隊等待,直到空出連結。 4.如果
基於Druid資料庫連線池的資料來源配置,資料庫連線密碼加密解密
Druid的資料庫連線池配置。 <!-- 基於Druid資料庫連線池的資料來源配置 --> <bean id="dataSource" class="com.alibaba.drui
【SaaS】SaaS只能靠大企業掙錢?終於有人證明它錯了!
作者:中國軟體網CEO曹開彬SaaS只有針對大型企業才能贏利。在我印象中,應該從2015年開始,
資料庫連線池到底應該設定多大?
開發十年,就只剩下這套架構體系了! >>>
c3p0資料庫連線池如何正確的關閉資源(“too many connections”的解決辦法)
一.問題分析 關於c3p0資料庫連線池的資源的關閉是一個很重要的問題,但是資源的關閉不僅僅是隻呼叫close()方法,將連結放入池中那麼簡單,如果你不考慮資料來源DataSource的關閉,那麼你的Demo將在很少的資料庫互動之後報出“too many connection
[總結]資料庫連線池設定不當導致大量的本地連接出現time_wait狀態。
最近在做效能測試時,在使用netstat命令檢視本地網路連線狀態時發現有大量的連線處於time_wait狀態。 於是認為是我們的dbcp的配置檔案寫的有問題,開始查應如何配置dbcp。但是改了幾個引數後,發現還是出現大量的time_wait。 於是又開始檢視
資料庫連線池介紹、主要引數設定、作用
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName"
SQLServer的最大連線數 的測試 和資料庫連線池
很多做架構設計、程式開發、運維、技術管理的朋友可能或多或少有這樣的困惑: SQLServer到底支援多少連線數的併發?SQLServer是否可以滿足現有的應用嗎?現有的技術架構支援多少連線數的併發?在硬體效能與網路效能足夠理想的情況下理論上可以支援多少併發連線?
設定JDBC資料庫連線池
前言 JDBC作為J2EE的規範之一,它定義了連線資料庫的介面,封裝了連線資料庫的細節問題,給開發人員極大的方便。開發人員只需要實現這些介面,就可以連線不同的資料庫,不需要針對不同資料庫寫不
Java第三十三天(事務&資料庫連線池&DBUtils)
事務 Transaction 其實指的一組操作,裡面包含許多個單一的邏輯。只要有一個邏輯沒有執行成功,那麼都算失敗。 所有的資
Java資料庫連線池細節探討
我們知道,資料庫連線池可以把資料庫的連線快取起來,下次使用的話可以直接取到快取起來的資料庫連線。那麼,在這個過程中有幾個細節需要注意: 1、資料庫的連線數有沒有限制? 2、資料庫會不會自動斷開已經建立的連線? 3、如果資料庫重啟了,但應用沒有重啟,那麼資料庫連線池中的所有連線都不可用了,
JavaWeb_day10_資料庫連線池_c3p0_DBUtils
1.在實際開發中,“獲取連線”和“釋放資源”是很消耗系統資源的兩個過程,為了解決此問題,採用連線池技術,共享連線connection 2.連線池概念 3.Java的資料庫連線池的公共介面:javax.sql.DataSource 常見的連線池:DBCP C3
手寫資料庫連線池附gp連線jar包地址
手寫資料庫連線並,測試. 最近資料庫要連線GP資料庫(GreenplumSQL),在建立連線的時候需要做建立不同的連線數量. 其實當想到寫資料庫連線時,完全可以通過springdata jpa直接寫介面,這是一種思路. 所以在使用的使用,就寫了個dem
JDBC資料庫連線池連線資料庫及資料庫操作DAO層設計通用更新及查詢方法(二)
上篇文章主要介紹了通過資料庫連線池連線資料庫,然後設計了對資料庫通用更新和查詢方法,本篇文章主要通過例項介紹上篇文章定義的對資料庫操作的幾個方法的使用: 首先我們先在資料庫建立一個學生資訊表Student欄位如圖: 建立好表將配置檔案的資訊改好然後需要建立一
JDBC資料庫連線池連線資料庫及資料庫操作DAO層設計通用更新及查詢方法(一)
該篇文章介紹了資料庫連線池獲取資料庫連線以及資料庫操作的基本使用,然後主要提供了java專案案例中dao層的一種設計,利用反射的原理定義了通用的查詢方法可以對應所有的表和例項。文章中的每段程式碼都提供了詳細的註釋及邏輯步驟 首先匯入資料庫連線的所需要的jar包:
springboot配置預設資料庫連線池並解決初始連線未生效問題
目前Spring Boot中預設支援的連線池有dbcp,dbcp2, tomcat, hikari三種連線池。 在springboot1.5之前預設tomcat連線池, 版本org.springframework.boot.autoconfigure.jdbc.Data
JFinal配置資料庫連線池外掛和表類對映
配置資料庫連線池外掛,此處以Druid為例,還需要配置資料庫訪問外掛,即ActiveRecord外掛,用於建立資料庫中Table和Java Bean的mapping對映: public void configPlugin(Plugins me) { // 配置 druid