Connection Management and Monitoring#

本章延續 JDBC Connection Management 的概念,說明 Hibernate 的 connection provider 機制,以及如何監控 SQL statement 的執行。由於 JPA provider 會代替開發者產生 SQL,因此監控這些 statement 的行為與效能變得格外重要。

JPA Connection Management#

  • JPA 1.0 的設計高度依賴 Java EE application server,所有 database connection 由 application server 管理,提供 connection poolingmonitoringJTA 功能
  • 在 Java EE 環境中,DataSource 透過 JNDI 定位,開發者需在 persistence.xml 中指定 JNDI 名稱
  • JTA transaction 使用 jta-data-sourceRESOURCE_LOCAL transaction 使用 non-jta-data-source
  • 獨立應用程式(stand-alone)通常透過 dependency injection 設定 DataSource,而非 JNDI

自 JPA 2.0 起,標準化了以下連線設定屬性:

  • javax.persistence.jdbc.driver – Driver class name
  • javax.persistence.jdbc.url – 連線 URL
  • javax.persistence.jdbc.user – 使用者名稱
  • javax.persistence.jdbc.password – 密碼

這些屬性本身不足以滿足企業應用的需求,connection pooling 和 monitoring 仍屬於 JPA 實作層級的議題,需要依靠 Hibernate 的 connection provider 機制。

Hibernate Connection Providers#

Hibernate 定義了 ConnectionProvider 介面(org.hibernate.engine.jdbc.connections.spi.ConnectionProvider)作為連線工廠的抽象,支援 JDBC Driver 連線、RESOURCE_LOCAL 以及 JTA DataSource 等多種組態方式。

DriverManagerConnectionProvider#

  • 當設定 JPA 2.0 標準屬性或 Hibernate 專屬屬性(如 hibernate.connection.driver_classhibernate.connection.url 等)時,Hibernate 會選用此 provider
  • 雖然透過 DriverManager 取得連線並使用簡易的 pooling 實作來減少開銷,但 Hibernate 官方不建議在 production 環境使用此 provider

C3P0ConnectionProvider#

  • C3P0 是成熟的 connection pooling 方案,只需設定至少一個 hibernate.c3p0 前綴的屬性即可啟用
  • C3P0(2001)與 Apache DBCP(2002)是最早期且廣泛部署的 Java connection pool
  • 後來 BoneCP(2010)出現,但目前已被 HikariCP 取代

HikariCPConnectionProvider#

  • HikariCP 是目前最快的 connection pool 之一,Hibernate 5 透過 hibernate-hikaricp dependency 原生支援
  • 透過設定 hibernate.connection.provider_classorg.hibernate.hikaricp.internal.HikariCPConnectionProvider 啟用
  • 使用專屬的 hibernate.hikari.* 屬性進行設定(如 dataSourceClassNamedataSource.urlmaximumPoolSize 等)

DatasourceConnectionProvider#

  • 當 JPA 設定檔中定義 non-jta-data-sourcejta-data-source,或指定 hibernate.connection.datasource 時,選用此 provider
  • 相容於 JTA transaction(Java EE 環境中的必要條件)
  • 與 Spring 的 stand-alone JTA transaction manager(如 Bitronix、Atomikos)及 Java EE DataSource(s) 皆相容

即使使用 HikariCP,DatasourceConnectionProvider 仍是首選,因為外部設定 DataSource 提供了更大的彈性,可方便地加入 connection pooling、monitoring 與 logging 等裝飾功能。

Connection Release Modes#

Hibernate 延遲取得 database connection,直到當前 transaction 執行第一個 SQL statement 時才從 pool 取得連線。連線釋放策略透過 hibernate.connection.release_mode 設定:

  • after_transaction – 連線在 transaction commit 或 rollback 後才釋放
  • after_statement – 每次 statement 執行完畢後釋放連線,下次執行前重新取得。此策略是為了避免 application server 誤判 EJB 呼叫之間的 connection leak
  • auto(預設值)– RESOURCE_LOCAL 使用 after_transaction,JTA 使用 after_statement

after_statement 模式因頻繁的 connection 取得/釋放循環,會產生明顯的效能損耗。測試顯示 statement 數量越多,after_statementafter_transaction 的差距越大。若 JTA transaction management 不會干擾連線釋放策略,建議改用 after_transaction 以獲得更好的效能。

Figure 8.1: Connection release mode

Monitoring Connections#

使用外部設定的 DataSource 是推薦做法,因為可以透過 proxy 模式裝飾 DataSource,加入 connection pooling、monitoring 與 logging 功能。FlexyPool 就是採用此架構,透過 proxy 攔截 connection 的取得與釋放請求來監控使用情況。

Figure 8.2: FlexyPool architecture

Hibernate Statistics#

  • Hibernate 內建 statistics collector,可蒐集 database connection、Session transaction 和 second-level cache 使用的相關通知
  • StatisticsImplementor 介面定義了各種 Hibernate 內部事件的攔截契約(如 openSessionflushconnectprepareStatementendTransaction 等)
  • 預設停用,需設定 hibernate.generate_statisticstrue 啟用
  • 搭配 logger 設定(org.hibernate.engine.internal.StatisticalLoggingSessionEventListener 設為 info),每次 Session 結束時會輸出效能報告

Figure 8.3: Hibernate StatisticsImplementor interface

Customizing Statistics#

  • 預設的 statistics collector 僅計算 callback 呼叫次數,開發者可自訂 StatisticsImplementor 實作
  • 可透過繼承 ConcurrentStatisticsImpl 並覆寫 connectendTransaction 方法,加入自訂指標
  • 透過 StatisticsFactory 介面與 hibernate.stats.factory 設定屬性註冊自訂實作
  • 建議使用成熟的 metrics 框架(如 Dropwizard Metrics),支援 reservoir sampling、多種 reporter(SLF4J、JMX、Ganglia、Graphite)等功能

對於高效能的 data access layer,statistics 和 metrics 是必要條件。Hibernate statistics 機制讓開發團隊能深入了解 Hibernate 的內部運作。

Statement Logging#

ORM 工具會自動產生 DML statement,開發者有責任驗證這些 statement 的正確性與效能影響。SQL statement logging 應從開發初期就啟用。

實作業務邏輯時,Definition of Done 應包含對所有相關 data access 操作的審查。及早遵循此規則,可在部署到 production 時省去大量問題排查。

Hibernate 提供以下 SQL logging 設定屬性:

  • hibernate.show_sql – 將 SQL 印到 console(應避免使用,改用 logging framework)
  • hibernate.format_sql – 格式化 SQL statement 以提升可讀性
  • hibernate.use_sql_comments – 在自動產生的 SQL 中加入註解

透過 logger 設定 org.hibernate.SQLdebug 層級,可啟用 statement logging。由於 Hibernate 使用 PreparedStatement,log 中的 bind parameter 會顯示為 ? 佔位符。

Statement Formatting#

  • 設定 hibernate.format_sqltrue 可將 SQL 格式化為多行可讀格式
  • 僅適用於開發階段,production 環境的 log 通常需要集中解析與彙整,多行格式會影響 log parsing
  • 此設定僅影響 logged statement,實際送往 JDBC Driver 的 SQL 仍為單行文字
  • 透過外部 DataSource proxy 記錄的 statement 不受此設定影響

Statement-level Comments#

  • 設定 hibernate.use_sql_commentstrue,Hibernate 會在 SQL 中附加說明性註解
  • 有助於理解:觸發 statement 的 entity state transition、選擇 join 的原因、使用的 locking 機制
  • 與 formatting 不同,SQL comments 會傳播到底層 JDBC Driver,因此在 production 環境建議停用以減少網路開銷

Logging Parameters#

由於 PreparedStatement 的 bind parameter 不會出現在 log 中,需要透過 proxy JDBC DriverDataSource 來攔截 statement 執行並記錄實際參數值。

DataSource-proxy#

  • 提供自訂 JDBC statement execution listener 的支援
  • 採用程式化設定,適合 Spring 應用程式的 Java-based configuration
  • 可記錄 batch insert 的完整參數,並將同批次的 statement 群組顯示
  • 支援自訂 statement listener,可用於建構 query count validator,在開發階段防止 N+1 query 問題

datasource-proxy 的 custom statement listener 機制可用來建構 query count validator,在開發階段即時偵測並預防 N+1 query 問題。

Figure 8.4: DataSource-Proxy Architecture

P6Spy#

  • 2002 年發布,支援 declarative configuration(透過 spy.properties 檔案)
  • 可同時 proxy JDBC Driver(適合 Java EE)和 JDBC DataSource(適合 Spring)
  • 輸出包含:Timestamp、Execution time、Category、Connection ID、Original statement、Formatted statement(含實際 bind values)
  • 提供 outagedetection 設定,可偵測 long-running statements

Figure 8.5: P6Spy Architecture