(15) Spring Boot 連接至 PostgreSQL 與 HikariCP 的常見使用設定

Albert Hg
learning-from-jhipster
14 min readNov 20, 2020

在上一篇文章中,我們將專案導入了 H2 資料庫,並且透過 Spring Boot 提供的 JDBC Starter 搭配在 Dev 環境 Properties 中設定 spring.datasource 與 H2 資料庫進行連接。

因此本篇文章將會展示如何在 Prod 環境中設定相關參數,又需要導入哪些其他 Dependencies,才可以將專案與 PostgreSQL 進行連線。

不過因為節省資料庫的建置過程,因此本篇文章將會使用一個免費的「PostgreSQL Database Hosting Service」,透過這個服務,我們就可以簡單又快速的建立一個 Database Service 供我們進行連線測試。

不過如果你在你的本機或你的環境中已經有架設好 PostgreSQL,那麼就可以省略這項服務的使用,可以直接參考連線設定的部分即可。

免費的線上 PostgreSQL 服務 - ElephantSQL

這裡廢話不多說 XD,他就是一個有免費方案的線上 PostgreSQL 服務,使用簡單,操作介面方便又直覺,免費方案給的大方,又無需事先綁定信用卡 (這樣就不用怕流量不小新爆掉了),限制的部分整理如下:

  • 一個 Instance 最多可以存放 20 MB 的資料
  • 一個 Instance 最多可以同時有 5 個 Connections

對於測試來說,這已經非常足夠了。那麼接著我們就來開始創建第一個免費的 Instance 吧!

註冊帳號與建立第一個 Instance

首先要先創建一個帳號,他有支援 Github 與 Google 的 Social login,或者要直接 Sign up 也可以。

https://customer.elephantsql.com/login

接著就可以看到 Instances 的列表,因為才剛建立帳號而已,所以裡面不會有任何東西。

https://customer.elephantsql.com/instance/

點擊右上方的「Create New Instance」,接著設定 Instance 的名稱。值得注意的是,這裡的名稱只是 Instance 的名稱,而不是資料庫的名稱。另外,在 Plan 的部分預設是 Free 的方案,而其他的方案都是要收費的。

https://customer.elephantsql.com/instance/create

接著要選取 Data Center 的位置,但因為是免費方案,所以可以選擇的位置就會比較少。在這邊我們選取離台灣比較近的「AP-East-1 (Hong Kong)」。

最後再檢查一次內容,沒有問題我們就直接「Create Instance」。頁面跳轉回 Instance 列表頁面,剛剛所建立的 Instance 就會出現在列表中了。

https://customer.elephantsql.com/instance/

查看 Instance 的相關資訊

(為了做示範,在這裡我的連線資訊全部都會以明碼顯示,但是我會在範例結束之後刪除這個 Instance)

從列表點擊對應的 Instance 後,就會進入到 Details 的頁面。

在 Details 頁面中你就可以看到這個資料庫的基本資訊。ElephantSQL 預設是將 Database 的名稱與連線許可的名稱視為同一個值。而 URL 的部分則是像 postgres://[username]:[password]@[hostname]/[databasename] 這樣的格式來表示,對應的結果就為:

  • username: aeafcpwv
  • password: izbnpJkJ3_aaEhcSkbB5pUNaa5mq8rjn
  • hostname: john.db.elephantsql.com:5432
  • databasename: aeafcpwv

其他功能

在左邊有其他的頁面可以進入,只是如果是使用免費的 Instance 的話,那麼會有一些頁面是沒有權限可以造訪的。

在免費版中,可以進入的頁面有:

  • Detail:資料庫的連線相關細節
  • Browser:使用 SQL Query 的頁面
  • Stats:查看資料庫的連線狀態,像是連接數量與連接位置
  • Slow Query:顯示 SQL Query 的時間以及相關細節
  • Backup:備份這個 Instance 的資料

那麼 ElephantSQL 的部分就暫時先介紹到這邊,不過應該可以發現,他的操作介面實在非常簡單又直覺,也因此如果需要一個資料庫來進行測試且資料量不大的話,ElephantSQL 應該可以算是個不錯的選擇。

JDBC 連接至 PostgreSQL

現在我們得到一個遠端的 PostgreSQL 服務,接著要將專案進行設定,並且將專案連到這個 PostgreSQL 的遠端服務。

在上一篇文章中,我們是在 Dev 環境的 Properties 內,對 spring.datasource的相關設定值指向 H2 Database。因此現在在這一篇文章中,我們可以改為在 Prod 的環境中加上與 PostgreSQL 的相關連線設定值。

根據在 ElephantSQL 的 Detail 頁面中的資訊,我們可以進行如下設定:

spring:
 datasource:
  url: jdbc:postgresql://john.db.elephantsql.com:5432/aeafcpwv
  username: aeafcpwv
  password: izbnpJkJ3_aaEhcSkbB5pUNaa5mq8rjn

接著以 Prod 的 Properties 啟動專案

.\mvnw -P prod

其中可以看到 HikariCP 的相關訊息,這代表著 Hikari 在建立 Connection Pool (別忘了,Spring Boot 預設所使用的 Connection Pool 就是 Hikari):

INFO [main] HikariDataSource: HikariPool-1 - Starting...
INFO [main] HikariDataSource: HikariPool-1 - Start completed.

接著我們可以到 ElephantSQL 的 STATS 頁面檢查連線狀態:

由於免費版的 Instance 可以允許的連線數量是 5 個,在 Application Name 的欄位上,第一列顯示「elephantsql-stream」,這是因為在建立 Instance 後,ElephantSQL 會需要佔據一個連線位置,以提供與事件或訊息之間的溝通。

而下方的四個連線,就是透過 HikariCP 所建立的 Connection Pool。但其實 HikariCP 所建立的 Connection Pool 並不只有四個而已。可以在 HikariCP 的 Github 中找到 maximumPoolSize 的相關訊息,他的預設值為 10,所以在不設定任何參數的情況下,HikariCP 所建立的 Connection Pool 的數量是 10 個。

如果想要修改連線數量,可以像下方這樣設定,假設我只要建立 3 個 Connection Pool:

spring:
 datasource:
  url: jdbc:postgresql://john.db.elephantsql.com:5432/aeafcpwv
  username: aeafcpwv
  password: izbnpJkJ3_aaEhcSkbB5pUNaa5mq8rjn
  hikari:
   maximum-pool-size: 3

那麼在 ElephantSQL 的 STATS 頁面中,就可以看到只有建立 3 個連線了。

Idle 是甚麼

Idle,形容詞,閒置的。在上圖中可以看到在 Stats 的欄位,狀態都是 Idle,代表他的連線狀態是閒置的,那為什麼會需要閒置的連線呢?

還記得在建立 Connection Pool 的目的就是在於「當有需要使用連線的時候就可以從 Connection Pool 中取得 Connection」,而這些 Connection 要可以「隨取即用」就必須要讓 Connection 保持「長連接」的狀態,或者說是「Keep Alive」。

要讓連線是長連接的狀態就必須要在建立 TCP 連線後,相互的定時通知對方說「欸欸!我還活著喔!不要斷掉了!」,這個傳送的內容,就叫做「Heartbeat Packet」,或者稱他為「心跳包」。

可是在這樣頻繁的送心跳包的過程中,也都是需要對心跳包做編碼與解碼,因此多少來是會浪費系統資源,因此 Idle 機制就出現了。Idle 機制算是心跳包的機制優化,為了避免相互的、定時的持續送出心跳包,那麼只要在服務端去監聽客戶端對服務端的讀寫時間,並定期的去跟客戶端確認連線狀態,如此一來,就可以用相對少的資源來達到長連接的機制了。

之所以會先介紹 Idle,主要是因為後續在 Hikari 的設定值中,有相關的參數可以進行設定,因此先介紹過甚麼是 Idle Connection 以及 Idle Timeout 會對於後續的了解內容有幫助。

Hikari 的常用的參數設定

如果仔細的參考 Hikari Github 上的說明,裡頭有提到一些比較常用的設定,這裡我們稍微簡單的帶過 (以下 Connection Pool 都會以「連線池」表示):

poolName

可以設定連線池的名稱,主要的作用在於可以在 Console 或者 Print Log 的時候,能夠顯示自定義的連線池的名稱。

maximumPoolSize

設定 Hikari 建立連線池內 Connection 的最大數量。而這個值的設定其實應該依照你的執行環境,例如硬體性能。此外,如果設定的值超過資料庫所許可的連線數量,那麼連線池內的數量就會依照許可連線數而定。

autoCommit

設定是否要自動 Commit SQL Command,預設值為 true。如果 autoCommit 的值為 true,那麼只要在使用這些 Connect 的 Statement 或 Session 去操作資料庫,例如新增、修改、刪除等命令都會被自動提交,如果不小心發生邏輯錯誤,那麼所提交出去的命令也就無法挽回了。

但如果 autoCommit 的值設定為 false,那麼則必須明確的在商業邏輯後提交所使用到的命令後才會使 Commit 生效,而在商業邏輯中若發生錯誤,則可以使用 Rollback 來回復在發生錯誤之前的狀態。為了避面錯誤發生時無法返回狀態,也因此我們通常會將此值設定為 false,像這樣:

spring:
 datasource:
  hikari:
   auto-commit: false

connectionTimeout

當從連線池拿取 Connection 時,卻發現 Connection 的數量已達上限,那麼就會等待一段時間,如果在這段時間內都沒有 Connection 可以從連線池中取得,那麼就會發生 SQLException 的錯誤。而這段等待時間的最大許可值就是 connectTimout,其預設值為 30000,也就是 30 秒。

minimumIdle

這個值可以控制 Idle Connection 的最小數量,但這個值的預設值會取決於 maximumPoolSize 的大小。若 minimumIdle 的數量小於 maximumPoolSize ,那麼在建立連線池的步驟就會像這樣:

  • 先建立 minimumIdle 數量的 Idle Connection 於連線池中
  • 應用程式向連線池拿取 Connection
  • 當拿取 Connection 的數量大於 minimumIdle 時,Hikari 會建立更多的 Connection 於連線池中,直到連線池內的數量為 maximumPoolSize 為止
  • 當連線池中的 Idle Connection 所閒置的時間超過 idleTimeout,則關閉連線,直到連線池內的數量為 minimumIdle 為止

這個值代表如果設定這個值,則會讓連線池的數量不固定,並且會造成額外開啟連線與關閉連線所需的資源。因此除非是在其他特定需求的情況下,否則這個值我們就會不設定他,讓他是預設值,也就是 maximumPoolSize,即可。

idleTimeout

這個值的說明可以看 minimumIdle 部分的說明。而 idleTimeout 的設定也只有在 minimumIdle < maximumPoolSize 的情況下才有作用。

maxLifetime

這個值在官方網站的說明中是「強烈建議使用的值」。這個值主要用來設定在連線池的 Connection 所能存活的最大時間。當 Connection 存活的時間到達了 maxLifetime,那麼這個 Connection 就會被關閉,然後再重新建立一個新的 Connection 於連線池中。

之所以會這麼做的原因是為了要避免 mass-extinction 的發生,同時也可以避免堆積過多的 Garbage 等待 GC 回收,造成 GC 回收時間過久,導致系統發生異常。

詳細說明可以參考: https://juejin.cn/post/6844904146915557389

connectionTestQuery

這是連線池在建立前,會先執行此值所設定的 SQL 命令。官方建議如果是使用 JDBC4 的 Driver,那麼最好不要設定此值。

文末

上一篇文章中,我們在 Dev 環境中將應用程式與 H2 DB 連接。而在本篇中,我們則是在 Prod 環境中將應用程式與 PostgreSQL 進行連接,並且使用線上的免費資源 ─ ElephantSQL 來進行測試。

中間有稍微提到了一下 Keep Alive 的實現機制,並簡單的介紹了一下甚麼是 Idle Connection。而最後則是介紹了 Hikari 的一些常用設定值。

在下一篇開始,我們就要開始將資料庫建立 Table,但是在那之前會先介紹資料庫的版本管理工具 ─ Liquibase,透過 Liquibase 來協助我們避免直接寫 SQL Command 來新增、修改、刪除 Table 的相關動作,並簡介資料庫版本控管的相關細節。

--

--

Albert Hg
learning-from-jhipster

I am a programmer but love other things. I am a nobody but keep myself going. I am a person who wishes to reach the heaven but lost the wings.