Skip Navigation
Show nav
Dev Center
  • Get Started
  • ドキュメント
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
    • .NET
  • ドキュメント
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Heroku Blog

    Find out what's new with Heroku on our blog.

    Visit Blog
  • Log inorSign up
View categories

Categories

  • Heroku のアーキテクチャ
    • Dyno (アプリコンテナ)
      • Dyno Management
      • Dyno Concepts
      • Dyno Behavior
      • Dyno Reference
      • Dyno Troubleshooting
    • スタック (オペレーティングシステムイメージ)
    • ネットワーキングと DNS
    • プラットフォームポリシー
    • プラットフォームの原則
  • Developer Tools
    • コマンドライン
    • Heroku VS Code Extension
  • デプロイ
    • Git を使用したデプロイ
    • Docker によるデプロイ
    • デプロイ統合
  • 継続的デリバリーとインテグレーション
    • 継続的統合
  • 言語サポート
    • Node.js
      • Working with Node.js
      • Node.js Behavior in Heroku
      • Troubleshooting Node.js Apps
    • Ruby
      • Rails のサポート
      • Bundler の使用
      • Working with Ruby
      • Ruby Behavior in Heroku
      • Troubleshooting Ruby Apps
    • Python
      • Working with Python
      • Python でのバックグランドジョブ
      • Python Behavior in Heroku
      • Django の使用
    • Java
      • Java Behavior in Heroku
      • Working with Java
      • Maven の使用
      • Spring Boot の使用
      • Troubleshooting Java Apps
    • PHP
      • PHP Behavior in Heroku
      • Working with PHP
    • Go
      • Go の依存関係管理
    • Scala
    • Clojure
    • .NET
      • Working with .NET
  • データベースとデータ管理
    • Heroku Postgres
      • Postgres の基礎
      • Postgres スターターガイド
      • Postgres のパフォーマンス
      • Postgres のデータ転送と保持
      • Postgres の可用性
      • Postgres の特別なトピック
      • Migrating to Heroku Postgres
    • Heroku Data For Redis
    • Apache Kafka on Heroku
    • その他のデータストア
  • AI
    • Working with AI
    • Heroku Inference
      • Inference API
      • Quick Start Guides
      • AI Models
      • Inference Essentials
    • Vector Database
    • Model Context Protocol
  • モニタリングとメトリクス
    • ログ記録
  • アプリのパフォーマンス
  • アドオン
    • すべてのアドオン
  • 共同作業
  • セキュリティ
    • アプリのセキュリティ
    • ID と認証
      • シングルサインオン (SSO)
    • Private Space
      • インフラストラクチャネットワーキング
    • コンプライアンス
  • Heroku Enterprise
    • Enterprise Accounts
    • Enterprise Team
    • Heroku Connect (Salesforce 同期)
      • Heroku Connect の管理
      • Heroku Connect のリファレンス
      • Heroku Connect のトラブルシューティング
  • パターンとベストプラクティス
  • Heroku の拡張
    • Platform API
    • アプリの Webhook
    • Heroku Labs
    • アドオンのビルド
      • アドオン開発のタスク
      • アドオン API
      • アドオンのガイドラインと要件
    • CLI プラグインのビルド
    • 開発ビルドパック
    • Dev Center
  • アカウントと請求
  • トラブルシューティングとサポート
  • Salesforce とのインテグレーション
  • 言語サポート
  • Clojure
  • Clojure を使用したデータベース接続プール

Clojure を使用したデータベース接続プール

日本語 — Switch to English

この記事の英語版に更新があります。ご覧の翻訳には含まれていない変更点があるかもしれません。

最終更新日 2024年05月09日(木)

Table of Contents

  • アプリケーションの作成
  • c3p0 の使用
  • 接続プールの使用
  • Heroku へのアプリのデプロイ
  • プールの設定
  • PgBouncer を使用した接続の制限

接続プールは、事前に作成された一連の再利用可能な接続オブジェクトを使用してデータベースに接続するために、ソフトウェアアプリケーションによって使用されるパターンです。接続が必要になった場合は、このプールから既存の接続が取得されます。接続を使用しているスレッドが完了すると、その接続は、別のスレッドで使用できるようにプールに戻されます。このパターンにより、ネットワークトラフィックが減少し、新しい接続を作成するコストが抑えられ、さらにガーベジコレクターへの負荷が軽減されるためデータベースへの接続のオーバーヘッドが削減されます。

この記事では、Java Database Connectivity (JDBC) API と c3p0 ライブラリを使用してデータベース接続プールを作成する方法について学習します。

アプリケーションの作成

すでに Clojure アプリケーションがある場合は、それをこの例で使用できます。それ以外の場合は、先に進む前に「Heroku スターターガイド (Clojure)​」の記事から単純なアプリケーションを作成します。この例のソースコード​から始めて、順番に進めていくこともできます。

c3p0 の使用

c3p0 は、同名のキャラクター​と同様に、プロトコルを主に扱います。ただし、イウォークと話す代わりにデータベースプロトコルを変換します。c3p0 は、従来の DriverManager ベースの JDBC ドライバーを、JNDI バインド可能な DataSource で拡張します。これには、接続とステートメントのプーリングを実装する DataSource が含まれます。

この例では、対応する clojure/java.jdbc 拡張を持つ c3p0 を使用します。これらの依存関係を追加するには、プロジェクトの project.clj​ ファイルを開き、次のエントリを :dependencies​ リストに追加します。

[org.clojure/java.jdbc "0.3.6"]
[clojure.jdbc/clojure.jdbc-c3p0 "0.3.1"]

ファイルを保存し、Leiningen を実行して、ライブラリがダウンロードされていることを確認します。

$ lein install
Retrieving org/clojure/java.jdbc/0.3.6/java.jdbc-0.3.6.pom from central
Retrieving org/clojure/java.jdbc/0.3.6/java.jdbc-0.3.6.jar from central
Retrieving clojure/jdbc/clojure.jdbc-c3p0/0.3.1/clojure.jdbc-c3p0-0.3.1.pom from clojars
Retrieving clojure/jdbc/clojure.jdbc-c3p0/0.3.1/clojure.jdbc-c3p0-0.3.1.jar from clojars
...
Installed jar and pom into local repo.

次に、db.clj​ ファイルをプロジェクトに作成し、下記のコードを先頭に記述します (ただし、名前空間はアプリケーションに適した名前空間に置き換えます)。

(ns ticks.db
  (:import com.mchange.v2.c3p0.ComboPooledDataSource)
  (require [clojure.java.jdbc :as jdbc]
           [jdbc.pool.c3p0    :as pool]))

この下に、環境からデータベース URL を取得するための次の関数を追加します。

(def db-uri
  (java.net.URI. (or
    (System/getenv "DATABASE_URL")
    "postgresql://localhost:5432/ticks")))

この関数は DATABASE_URL 環境変数の値を取得しようとしますが、見つからない場合はデフォルトで localhost 接続文字列を使用します。これはローカル開発に役立ちます。

ここで、データベース URL を解析してユーザー名とパスワードを割り出す次の関数を追加します。

(def user-and-password
  (if (nil? (.getUserInfo db-uri))
    nil (clojure.string/split (.getUserInfo db-uri) #":")))

そして最後に、このモジュール用に、データベース接続を定義するための関数を追加します。

(def spec
  (pool/make-datasource-spec
    {:classname "org.postgresql.Driver"
    :subprotocol "postgresql"
    :user (get user-and-password 0)
    :password (get user-and-password 1)
    :subname (if (= -1 (.getPort db-uri))
                (format "//%s%s" (.getHost db-uri) (.getPath db-uri))
                (format "//%s:%s%s" (.getHost db-uri) (.getPort db-uri) (.getPath db-uri)))}))

この関数は、clojure.jdbc ライブラリの make-datasource-spec​ 関数を利用して接続プールを初期化します。これにより、spec​ 関数を使用してプールから接続を取得できます。

データベースを使用するコードがすでにアプリケーションにある場合、既存のコードの代わりに spec​ 関数の使用を開始できます。次のセクションでは、その使用方法の簡単な例を示します。

接続プールの使用

プロジェクトのメインクラス (スターターガイドの例)​を使用した場合は web.clj​ ファイル) を開き、少なくとも次の require ステートメントがあることを確認します。

(ns ticks.web
  (:require
    [clojure.java.jdbc :as jdbc]
    [ticks.db :as db]
    [compojure.core :refer [defroutes GET]]
    [compojure.handler :refer [site]]
    [compojure.route :as route]
    [ring.adapter.jetty :as jetty]
    [environ.core :refer [env]]))

ここで、データベーススキーマを移行する次の関数を追加します。

(defn migrated? []
  (-> (jdbc/query db/spec
    [(str "select count(*) from information_schema.tables "
    "where table_name='ticks'")])
    first :count pos?))

(defn migrate []
  (when (not (migrated?))
  (print "Creating database structure...") (flush)
  (jdbc/db-do-commands db/spec
    (jdbc/create-table-ddl
      :ticks
      [:id :serial "PRIMARY KEY"]
      [:body :varchar "NOT NULL"]
      [:tick :timestamp "NOT NULL" "DEFAULT CURRENT_TIMESTAMP"]))
      (println " done")))

これらの関数では、"ticks" という名前の単純なテーブルがまだ存在しない場合、作成します。jdbc​関数の引数として db/spec​ 関数をどのように使用しているかに注目してください。

ここで、新しいテーブルをクエリする次の関数を追加します。

(defn tick []
  (jdbc/insert! db/spec :ticks [:body] ["hello"]))

次に、tick​ 関数を呼び出すルートを定義してから、別のクエリを実行してティック数を表示します。

(defroutes app
  (GET "*" []
    (tick)
    {:status 200
      :headers {"Content-Type" "text/plain"}
      :body (str "Ticks: " (first (jdbc/query db/spec ["select count(*) from ticks"])))}))

最後に、アプリケーションの起動時に migrate​ 関数が必ず呼び出されるようにします。これは次のようになります。

(defn -main [& [port]]
  (migrate)
  (let [port (Integer. (or port (env :port) 5000))]
    (jetty/run-jetty (site #'app) {:port port :join? false})))

これで、アプリを実行できます。

Heroku へのアプリのデプロイ

アプリにまだデータベースがない場合、次のコマンドを実行して追加できます。たとえば、Essential-0 Heroku Postgres データベースを追加するには、次のようにします。

$ heroku addons:create heroku-postgresql:essential-0

次に、すべての変更を Git に追加し、アプリケーションをデプロイします。

$ git add .
$ git commit -m "adding a database connection pool"
$ git push heroku master

次に、ログを表示して、プールが正しく作成されていることを確認します。

$ heroku logs
2014-12-21T22:12:04.756975+00:00 heroku[web.1]: Starting process with command `java $JVM_OPTS -cp target/ticks-standalone.jar clojure.main -m ticks.web`
2014-12-21T22:12:05.262632+00:00 app[web.1]: Picked up JAVA_TOOL_OPTIONS: -Xmx384m  -Djava.rmi.server.useCodebaseOnly=true
2014-12-21T22:12:07.341079+00:00 app[web.1]: INFO: MLog clients using java 1.4+ standard logging.
2014-12-21T22:12:07.341072+00:00 app[web.1]: Dec 21, 2014 10:12:07 PM com.mchange.v2.log.MLog
2014-12-21T22:12:08.358960+00:00 app[web.1]: Dec 21, 2014 10:12:08 PM com.mchange.v2.c3p0.C3P0Registry
2014-12-21T22:12:08.358965+00:00 app[web.1]: INFO: Initializing c3p0-0.9.5-pre9 [built 08-October-2014 03:06:08 -0700; debug? true; trace: 10]
2014-12-21T22:12:10.579168+00:00 app[web.1]: Dec 21, 2014 10:12:10 PM com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource
2014-12-21T22:12:10.579174+00:00 app[web.1]: INFO: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1bqs26q96urtp65v8d9bo|3276732, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> org.postgresql.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceUseNamedDriverClass -> false, identityToken -> 1bqs26q96urtp65v8d9bo|3276732, idleConnectionTestPeriod -> 800, initialPoolSize -> 0, jdbcUrl -> jdbc:postgresql://ec2-54-204-42-119.compute-1.amazonaws.com:5432/ddan7chsh1kg3g, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 3600, maxIdleTime -> 1800, maxIdleTimeExcessConnections -> 120, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
2014-12-21T22:12:10.594560+00:00 app[web.1]: Dec 21, 2014 10:12:10 PM com.mchange.v2.resourcepool.BasicResourcePool
2014-12-21T22:12:10.594564+00:00 app[web.1]: WARNING: Bad pool size config, start 0 < min 3. Using 3 as start.
2014-12-21T22:12:10.760360+00:00 app[web.1]: 2014-12-21 22:12:10.759:INFO:oejs.Server:jetty-7.x.y-SNAPSHOT
2014-12-21T22:12:10.801601+00:00 app[web.1]: 2014-12-21 22:12:10.800:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:34833
2014-12-21T22:12:10.960005+00:00 heroku[web.1]: State changed from starting to up

“Bad pool size config” (プールサイズの設定が正しくない) という警告に気付くかもしれません。ライブラリによって適切なデフォルトが選択されるため、これは問題ありません。ただし、実際のアプリケーションでは、最小プールサイズやその他の多くのオプションも設定する必要があります。

プールの設定

プール内でウォーム状態に維持されるアイドル接続の数は、アプリケーションのサイズや性質によって異なります。多くのユーザーが、HTTP リクエストを処理するスレッドあたり 1 つの接続で十分であることに気付いています (HTTP リクエストを処理するスレッドが接続を使用する唯一のスレッドである場合)。アプリケーションが、接続を新しいスレッドにすぐに引き継げないほど非常に高いスループットを処理している場合は、さらに多くを必要とする可能性があります。または、すべての HTTP リクエストがデータベースへのアクセスを必要としているわけでない場合は、少なくて済む可能性があります。最終的には、本番環境の負荷の下でのアプリケーションのプロファイリングが適切なプールパラメータを決定するための最善の方法です。

開発環境では、データベースをチェックすることにより、アプリケーションによって使用される接続の数を確認できます。

$ psql -h localhost
psql (9.3.2)
Type "help" for help.
jkutner=# \q

これにより、開発データベースへの接続が開かれます。その後、次を実行して Postgres への接続の数を確認できます。

select count(*) from pg_stat_activity where pid <> pg_backend_pid() and usename = current_user;

これにより、そのデータベース上の接続の数が返されます。

 count
-------
   5
(1 row)

シミュレートされた本番環境の負荷の下で、これは必要なプールのサイズを適切に示しています。ただし、いくつかの制約があります。

データベース接続の最大数

Heroku では、マネージド Postgres​ データベースが提供されます。階層型データベースごとにさまざまな接続制限があります。これは、Heroku Postgres アドオンのドキュメント​にある一覧で見つけることができます。低い層のデータベースでは、高い層のデータベースより少ない接続が許可されます。データベースは、アクティブな接続の最大数に達すると、新しい接続を受け付けなくなります。これにより、アプリケーションが接続タイムアウトになり、例外が発生する可能性があります。

スケールアウトするときは、アプリケーションに必要なアクティブな接続の数に注意することが重要です。各 dyno で 5 つのデータベース接続が許可されている場合は、より堅牢なデータベースのプロビジョニングが必要になるまでに、4 つの dyno にしかスケールアウトできません。

これで、接続プールを設定する方法や、データベースで処理できる接続の数を見つける方法がわかったので、各 dyno に必要な接続の適切な数を計算する必要があります。

PgBouncer を使用した接続の制限

データベース接続の制限に達するまで、追加の dyno を使用して引き続きアプリケーションをスケールアウトできます。この時点に達する前に、PgBouncer buildpack​ を使用して、各 dyno に必要な接続の数を制限することをお勧めします。

PgBouncer では、データベーストランザクションで共有される接続のプールが保持されます。これにより、Postgres への接続 (通常は開いており、アイドル状態) が最小限に維持されます。ただし、トランザクションプーリングでは、名前付きプリペアドステートメント、セッションアドバイザリロック、リッスン/通知、またはセッションレベルで操作するその他の機能を使用できなくなります。詳細は、「PgBouncer buildpack FAQ for full list of limitations​」(制限の完全なリストに関する PgBouncer buildpack の FAQ) を参照してください。

多くのフレームワークでは、PgBouncer を使用するためにプリペアドステートメントを無効にする​必要があります。その後、PgBouncer buildpack を使用するようにアプリを設定します。

JDBC の場合、これには接続文字列への prepareThreshold=0​ の追加が必要です。ただし、JDBC ドライバへのパッチの適用​も必要になることがあります。

プリペアドステートメントを無効にするか、またはフレームワークでそれが使用されていないことを確認する前に続行しないでください。

$ heroku buildpacks:add heroku/pgbouncer

次に、アプリケーションを確実に実行可能にする必要があるため、言語固有の buildpack を追加する必要があります。Clojure を使用しているため、次のようになります。

$ heroku buildpacks:add heroku/clojure

ここで、PgBouncer を起動するように Procfile​ を変更する必要があります。Procfile​で、コマンド bin/start-pgbouncer-stunnel​ を web​ エントリの先頭に追加します。そのため、Procfile​ が

web: java $JVM_OPTS -cp target/ticks-standalone.jar clojure.main -m ticks.web

であった場合は、次のようになります。

web: bin/start-pgbouncer-stunnel java $JVM_OPTS -cp target/ticks-standalone.jar clojure.main -m ticks.web

結果を Git にコミットし、ステージングアプリでテストした後、本番環境にデプロイします。

デプロイ時、出力には次の内容が表示されます。

=====> Detected Framework: pgbouncer-stunnel

Clojure、JDBC、c3p0 を使用した接続プーリングについての詳細は、clojure-doc.org の java.jdbc セクション​を参照してください。

この記事で使用されている例のソースコード​は、GitHub で見つけることができます。

関連カテゴリー

  • Clojure
Heroku スターターガイド (Clojure) Heroku の Clojure サポート

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure
  • .NET

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing
  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Github
  • LinkedIn
  • © 2025 Salesforce, Inc. All rights reserved. Various trademarks held by their respective owners. Salesforce Tower, 415 Mission Street, 3rd Floor, San Francisco, CA 94105, United States
  • heroku.com
  • Legal
  • Terms of Service
  • Privacy Information
  • Responsible Disclosure
  • Trust
  • Contact
  • Cookie Preferences
  • Your Privacy Choices