OCI8 接続のハンドリングおよびプーリング

接続関数

oci8 拡張モジュールは Oracle に接続するための 3 つの異なる関数を提供しています。標準の接続関数は oci_connect() です。これは Oracle データベースへの接続を作成し、 それ以降のデータベースで使うリソースを返します。

Oracle サーバーへの接続は、完了まで要する時間という点から見ると、 かなりコストのかかる操作です。oci_pconnect() 関数は、 異なるスクリプトリクエスト間で接続の再利用が可能な 持続的キャッシュを使用します。 これは、PHP プロセス (もしくは Apache の子プロセス) 毎の接続に関するオーバーヘッドを一度のみ負うということを意味しています。

もしアプリケーションが信用された異なる Web ユーザー毎に Oracle に接続する場合、oci_pconnect() による持続的キャッシュは、 同時ユーザー数の増加と共に有効ではなくなるでしょう。 これは、多くのアイドル状態の接続が維持されることが原因で、 Oracle サーバー全体のパフォーマンスに不利な影響を与え始めるためです。 もしアプリケーションがこの方法で構成されている場合、 oci8.max_persistentoci8.persistent_timeout (持続的接続のキャッシュサイズや生存期間の制御が可能になります) を使用してアプリケーションをチューニングする、あるいは Oracle Database Resident Connection Pooling を使う (Oracle Database 11g 以降の場合)、もしくは oci_connect() を使用することが推奨されます。

oci_connect()oci_pconnect() の両者とも接続キャッシュを使用します。もし、同一パラメータと共に oci_connect() を複数回コールする場合、 2 番目以降は既存の接続ハンドルを返します。oci_connect() によって使用されるキャッシュは、スクリプト実行終了時、 もしくは明示的に接続ハンドルを閉じた時にクリアされます。 oci_pconnect() も同様の動作をしますが、 キャッシュは独立して維持され、リクエスト間で残存します。

このキャッシュ機能は忘れてはならないほど重要です。 それは、2 つのハンドルがトランザクション的に独立していない (実際には同じ接続なので、どのような種類の独立もありません) ためです。もしアプリケーションが 2 つの別々でトランザクション的に独立した接続を必要とする場合、 oci_new_connect() を使用すべきです。

PHP プロセス終了時に oci_pconnect() キャッシュは消去され、 データベース接続は全て閉じられます。 そのため、持続的接続を効果的に使用するには、 PHP は Apache のモジュールであるか、または FCGI によって使用されるか、 または同様のものでなければいけません。 PHP が CGI によって、またはコマンドラインを介して使用される場合、 持続的接続には oci_connect() 以上に全く利益がありません。

oci_new_connect() は、他の既存の接続が存在したとしても 常に Oracle サーバーへの新規接続を生成します。 特にアプリケーションの最も負荷が高い部分など、 高トラフィックな Web アプリケーションに対しては oci_new_connect() の使用を避けてください。

DRCP 接続プーリング

PHP 5.3 (PECL OCI8 1.3) 以降では、 Oracle 11g のデータベース常駐接続プーリング (DRCP) をサポートします。 DRCP によりデータベースマシンのメモリをより効率的に使用し、 高い拡張性が得られます。 DRCP を使うためにアプリケーションを変更する必要はないか、または 最小限です。

DRCP は、ごく少数のデータベーススキーマを使用し、データベース接続を短時間オープン状態に 保つアプリケーションに適しています。 その他のアプリケーションは、 Oracle のデフォルトの Dedicated データベースサーバープロセスか、または Shared サーバーを使用しなければいけません。

DRCP は3つの接続機能全てに有益ですが、 oci_pconnect() で接続を作成すると最高の拡張性が得られます。

OCI8 で DRCP を利用可能にするには、 PHP で使用する Oracle クライアントライブラリ、 及び Oracle データベースのバージョンが共に 11g 以降でなければいけません。

DRCP についてのドキュメントはいくつかの Oracle マニュアルに見つかります。 例えば、使用法の情報のために、 Oracle ドキュメントで » データベース常駐接続プーリングの構成 をご覧ください。 » DRCP ホワイトペーパー には、 DRCP についての 予備知識となる情報が含まれています。

DRCP を使用するには、 OCI8 1.3 以降のエクステンション及び Oracle 11g 以降のライブラリと共に PHP をビルドし、これらのステップを続けます。

  • データベース内の接続プールを開始するために、 特権を持つデータベース管理者として SQL*Plus のようなプログラムを使います。

        SQL> execute dbms_connection_pool.start_pool;
    

  • DRCP の設定を構成するために、 任意で dbms_connection_pool.alter_param() を使用します。 現行のプール設定は、 DBA_CPOOL_INFO ビューで照会できます。

  • 使用する接続文字列を更新します。 MYDB のようなネットワーク接続名を使って現在接続する PHP アプリケーションでは、

        $c = oci_pconnect("myuser", "mypassword", "MYDB");
    

    tnsnames.ora ファイルを修正して、 (SERVER=POOLED) 節を追加します。 例えば、

        MYDB = (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp) (HOST=myhost.dom.com)
               (PORT=1521))(CONNECT_DATA=(SERVICE_NAME=sales)
               (SERVER=POOLED)))
    

    あるいは、 PHP で Easy Connect 構文を修正して、サービス名の後に :POOLED を追加します。

        $c = oci_pconnect("myuser", "mypassword", "myhost.dom.com:1521/sales:POOLED");
    

  • php.ini を編集して、接続クラス名を選択してください。 この名前は、接続プールの論理的なディビジョンを指示し、 個別のアプリケーションごとにプーリングを分離するために使われます。 同一のユーザー名と接続クラスをもつ PHP アプリケーションは、 プール内の接続を共有できます。これにより、より大きな拡張性が得られます。

        oci8.connection_class = "MY_APPLICATION_NAME"
    

  • アプリケーションを実行して、 11g 以降のデータベースに接続します。

注意:

持続的接続のパフォーマンスを必要とする Oracle 10g を使うアプリケーションでは、 Oracle Shared サーバー(マルチスレッドサーバーとして既知)を使用することにより、 必要なデータベース・メモリー量を減らせます。 詳細は Oracle ドキュメントを参照してください。

DRCP 推奨事項と既知の制約

DRCP 接続に対するパスワードを変更すると、 ORA-56609: Usage not supported with DRCP というエラーで失敗します。 これは Oracle データベース 11g の制約に典拠が示されています。

OCI8 1.3 以降のエクステンションでは、ユーザーが持続的接続を直ちに閉じることができます。 これにより、接続リソースの使用量に対して、より良く制御することができます。 持続的接続を参照する PHP 変数が無い場合、例えば PHP ユーザー関数の終了後に、 持続的接続もすぐに自動的に閉じられます。 この時、コミットされなかったトランザクションを全てロールバックします。 持続的な接続に対するこれらの変更により、非持続的接続と同様にふるまいます。 それは、インターフェイスを単純化し、アプリケーションの一貫性と予知可能性をより大きくします。 過去の動作を保つためには、oci8.old_oci_close_semanticsOn に設定してください。

もし、Oracle データベースのバージョンが 11.1.0.6 なら、DRCP を使うためには、 Oracle bug 6474441 に対する Oracle データベース・パッチを適用しなければいけません。 そのパッチがないと、 ORA-01000: maximum open cursors exceeded や、 ORA-01001 invalid cursorORA-01002 fetch out of sequence のようなエラーが発生するかもしれません。 このバグは Oracle 11.1.0.7 以降では修正されました。

もし、Oracle 11.1.0.6 データベース・パッチが適用できない場合は、 代わりに下記の3つのワークアラウンドのいずれかを使用できます。

  • DRCP の代わりに、Oracle の Dedicated または Shared サーバーを使って接続します。
  • PHP の oci8.statement_cache_size を 0 に設定します。
  • データベース初期化パラメータファイルでイベントを設定します。 event="56699 trace name context forever, level 128"

Oracle データベース 11.1.0.7 および Oracle データベース 11.1.0.6 の Oracle バグ 6474441 に対するパッチにより、DRCP 接続を使った PHP アプリケーションで セッション生成時にセッションプロパティを設定するために データベースの LOGON トリガーを使用できます。 そのような設定の例は、NLS言語と日付表示形式です。

もし、Oracle 11.1.0.6 データベース・パッチが適用できない場合は、 LOGON トリガーを使う代わりに、 下記のワークアラウンドのいずれかを使用できます。

  • ログオンした後に、PHP アプリケーションのコードを用いて明示的にセッション・プロパティを設定します。
  • DRCP の代わりに、 Oracle Dedicated または Shared サーバーを使って接続します。

Apache や FCGI のプロセスを再起動すると、 PHP の持続的接続は自動的に再確立されます。つまり、PHP で LOGON トリガーが使えるのはセッション属性を設定している場合のみで、 アプリケーションごとのユーザー接続要求には使えないということです。 DRCP の場合はなおさらです。 プールサイズの自動調整もあるし、DRCP の認証時に LOGON トリガーが起動するからでもあります。