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

【追光者系列】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)

分享創造快樂