歡迎光臨
每天分享高質量文章

【追光者系列】HikariCP 連接池配多大合適(第一彈)?

點擊上方“芋道原始碼”,選擇“置頂公眾號”

技術文章第一時間送達!

原始碼精品專欄

 

  • 經驗值&FlexyPool;

  • Less Is More

    • 公式:connections =((core_count * 2)+ effective_spindle_count)

    • 公理:You want a small pool, saturated with threads waiting for connections.

  • Pool-locking 池鎖

  • 具體問題具體分析

  • 參考資料


首先宣告一下觀點:How big should HikariCP be? Not how big but rather how small!連接池的大小不是設置多大,不是越多越好,而是應該少到恰到好處。
本文提及的是客戶端的執行緒池大小,資料庫服務器另有不同的估算方法。

  • 1. 經驗值&FlexyPool;

  • 2. Less Is More

    • 2.1 公式:connections =((core_count * 2)+ effective_spindle_count)

    • 2.2 公理:You want a small pool, saturated with threads waiting for connections.

  • 3. Pool-locking 池鎖

  • 4. 具體問題具體分析

經驗值&FlexyPool;

我所在公司260多個應用的線上連接池預設經驗值是如下配置的:

maximumPoolSize: 20
minimumIdle: 10

而Hikari的預設值是maximumPoolSize為10,而minimumIdle強烈建議不要配置、預設值與maximumPoolSize相同。我公司maximumPoolSize基本上這個值將決定到資料庫後端的最大實際連接數,對此的合理價值最好由實際的執行環境決定;我公司保留minimumIdle的值(並不是不設置)是為了防止空閑很久時創建連接耗時較長從而影響RT。
不過我還是比較傾向作者的觀點,儘量不要minimumIdle,允許HikariCP充當固定大小的連接池,畢竟我相信追求極致的Hikari一定可以盡最大努力快速高效地添加其他連接,從而獲得最佳性能和響應尖峰需求

minimumIdle
This property controls the minimum number of idle connections that HikariCP tries to maintain in the pool. If the idle connections dip below this value and total connections in the pool are less than maximumPoolSize, HikariCP will make a best effort to add additional connections quickly and efficiently. However, for maximum performance and responsiveness to spike demands, we recommend not setting this value and instead allowing HikariCP to act as a fixed size connection pool. Default: same as maximumPoolSize

maximumPoolSize
This property controls the maximum size that the pool is allowed to reach, including both idle and in-use connections. Basically this value will determine the maximum number of actual connections to the database backend. A reasonable value for this is best determined by your execution environment. When the pool reaches this size, and no idle connections are available, calls to getConnection() will block for up to connectionTimeout milliseconds before timing out. Please read about pool sizing. Default: 10

HikariCP的初始版本只支持固定大小的池。作者初衷是,HikariCP是專門為具有相當恆定負載的系統而設計的,並且在傾向連接池大小於保持其運行時允許達到的最大大小,所以作者認為沒有必要將代碼複雜化以支持動態調整大小。畢竟你的系統會閑置很久麽?另外作者認為配置項越多,用戶配置池的難度就越大。但是呢,確實有一些用戶需要動態調整連接池大小,並且沒有就不行,所以作者就增加了這個功能。但是原則上,作者並不希望缺乏動態的大小支持會剝奪用戶享受HikariCP的可靠性和正確性的好處。

如果想要支持動態調整不同負載的最佳池大小設置,可以配合Hikari使用同為the Mutual Admiration Society成員的Vlad Mihalcea研究的FlexyPool。當然,連接池上限受到資料庫最優併發查詢容量的限制,這正是Hikari關於池大小的起作用的地方。然而,在池的最小值和最大值之間,FlexyPool不斷嘗試遞增,確保該池大小在服務提供服務的過程中動態負載是一直正確的。

FlexyPool是一種reactive的連接池。其作者認為確定連接池大小不是前期設計決策的,在大型企業系統中,需要適應性和監控是做出正確決策的第一步。

FlexyPool具有以下預設策略

  • 在超時時遞增池。此策略將增加連接獲取超時時的標的連接池最大大小。連接池具有最小的大小,並可根據需要增長到最大大小。該上限溢位是多餘的連接,讓連接池增長超過其初始的緩衝區最大尺寸。每當檢測到連接獲取超時時,如果池未增長到其最大上限溢位大小,則當前請求將不會失敗。

  • 重試嘗試。此策略對於那些缺少連接獲取重試機制的連接池非常有用。

由於本文主要談Hikari,所以FlexyPool請各位讀者自行閱讀
https://github.com/vladmihalcea/flexy-pool
https://vladmihalcea.com/
http://www.importnew.com/12342.html

Less Is More

眾所周知,一個CPU核心的計算機可以同時執行數十或數百個執行緒,其實這隻是操作系統的一個把戲-time-slicing(時間切片)。實際上,該單核只能一次執行一個執行緒,然後操作系統切換背景關係,並且該內核為另一個執行緒執行代碼,依此類推。這是一個基本的計演算法則,給定一個CPU資源,按順序執行A和B 總是比通過時間片“同時” 執行A和B要快。一旦執行緒數量超過了CPU核心的數量,添加更多的執行緒就會變慢,而不是更快。
某用戶做過測試(見參考資料),得到結論1個執行緒寫10個記錄比10個執行緒各寫1個記錄快。使用jvisualvm監控程式運行時,也可以看出來thread等待切換非常多。設計多執行緒是為了盡可能利用CPU空閑等待時間(等IO,等交互…),它的代價就是要增加部分CPU時間來實現執行緒切換。假如CPU空閑等待時間已經比執行緒切換更短,(執行緒越多,切換消耗越大)那麼執行緒切換會非常影響性能,成為系統瓶頸。

其實還有一些因素共同作用,資料庫的主要瓶頸是CPU,磁盤,網絡(記憶體還不算最主要的)。

公式:connections =((core_count * 2)+ effective_spindle_count)

effective_spindle_count is the number of disks in a RAID.就是磁盤列陣中的硬碟數,hard disk.某PostgreSQL專案做過測試,一個硬碟的小型4核i7服務器連接池大小設置為: 9 = ((4 * 2) + 1)。這樣的連接池大小居然可以輕鬆處理3000個前端用戶在6000 TPS下運行簡單查詢。

我們公司線上機器標準是2核,有需求可以申請4核、8核,16核一般不開。虛擬機一般都是執行緒,宿主機一般是2個邏輯CPU。虛擬機預設就一塊硬碟,物理機有十幾塊。

公理:You want a small pool, saturated with threads waiting for connections.

在公式的配置上,如果加大壓力,TPS會下降,RT會上升,你可以適當根據情況進行調整加大。這時考慮整體系統性能,考慮執行緒執行需要的等待時間,設計合理的執行緒數目。但是,不要過度配置你的資料庫

Pool-locking 池鎖

增大連接池大小可以緩解池鎖問題,但是擴大池之前是可以先檢查一下應用層面能夠調優,不要直接調整連接池大小
避免池鎖是有一個公式的:

pool size = Tn x (Cm – 1) + 1

T n是執行緒的最大數量,C m是單個執行緒持有的同時連接的最大數量。
例如,設想三個執行緒(T n = 3),每個執行緒需要四個連接來執行某個任務(C m = 4)。確保永不死鎖的池大小是: 3 x(4 – 1)+ 1 = 10。
另一個例子,你最多有8個執行緒(T n = 8),每個執行緒需要三個連接來執行一些任務(C m = 3)。確保死鎖永遠不可能的池大小是: 8×(3-1)+ 1 = 17

  • 這不一定是最佳池大小,但是是避免死鎖所需的最低限度。

  • 在某些環境中,使用JTA(Java事務管理器)可以顯著減少從同一個Connection傳回getConnection()到當前事務中已經儲存Connection的執行緒所需的連接數。

具體問題具體分析

混合了長時間運行事務和非常短的事務的系統通常是最難調整任何連接池的系統。在這些情況下,創建兩個池實體可以很好地工作(例如,一個用於長時間運行的作業,另一個用於“實時”查詢)。
如果長期運行的外部系統,例如只允許一定數量的作業同時運行的作業執行佇列,這是作業佇列大小就是連接池非常合適的大小。

最後,我要說的是:

連接池大家是綜合每個應用系統的業務邏輯特性,加上應用硬體配置,加上應用部署數量,再加上db硬體配置和最大允許連接數測試出來的。很難有一個簡單公式進行計算。連接數及超時時間設置不正確經常會帶來較大的性能問題,並影響整個服務能力的穩定性。具體設置多少,要看系統的訪問量,可通過反覆測試,找到最佳點。壓測很重要。

參考資料

https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing
https://blog.csdn.net/cloud_ll/article/details/29212003

666. 彩蛋

如果你對 HikariCP 併發感興趣,歡迎加入我的知識星球一起交流。


知識星球

目前在知識星球(https://t.zsxq.com/2VbiaEu)更新瞭如下 Dubbo 原始碼解析如下:

01. 除錯環境搭建
02. 專案結構一覽
03. API 配置(一)之應用
04. API 配置(二)之服務提供者
05. API 配置(三)之服務消費者
06. 屬性配置
07. XML 配置
08. 核心流程一覽

09. 拓展機制 SPI

10. 執行緒池


一共 60 篇++

赞(0)

分享創造快樂