<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/features.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'ja',
  ),
  'this' => 
  array (
    0 => 'features.persistent-connections.php',
    1 => '持続的データベース接続',
    2 => '持続的データベース接続',
  ),
  'up' => 
  array (
    0 => 'features.php',
    1 => '機能',
  ),
  'prev' => 
  array (
    0 => 'features.connection-handling.php',
    1 => '接続処理',
  ),
  'next' => 
  array (
    0 => 'features.commandline.php',
    1 => 'コマンドラインの使用法',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'ja',
    'path' => 'features/persistent-connections.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="features.persistent-connections" class="chapter">
 <h1 class="title">持続的データベース接続</h1>


 <div class="simplesect">
  <h3 class="title">持続的データベース接続って何？</h3>
  <p class="simpara">
   持続的接続は、スクリプトの実行終了時にも閉じられないリンクです。
   持続的接続が要求された時、
   PHP は(前もってオープンされたままになっている)
   同じ持続的接続が既にオープンされていないかどうかを確認します。
   そして、存在する場合には、それを再利用します。存在しない場合には、
   そのリンクを作成します。&#039;同じ&#039;接続とは、同じホスト、同じユーザー名、
   同じパスワード(利用可能な場合)でオープンされた接続のことを意味します。
  </p>
  <p class="simpara">
   特定の接続を要求する方法はありませんし、
   既存の接続と新しい接続のどちらを取得できるかを保証する方法もありません。
   (すべての接続が使用中の場合や、
   リクエストが別の接続プールを持つ、
   別のワーカーによって処理される場合があります)
  </p>
  <p class="simpara">
   したがって、PHP の持続的接続は、たとえば以下の目的には使えません:
  </p>
  <ul class="simplelist">
   <li>特定のデータベースセッションを特定のWebユーザーに割り当てる</li>
   <li>複数のリクエストにまたがる巨大なトランザクションを生成する</li>
   <li>あるリクエストでクエリを発行し、クエリの結果を別のリクエストで収集する</li>
  </ul>
  <p class="simpara">
   持続的接続は、
   持続的でない接続にできない機能は <em>全く</em>
   提供しません。
  </p>
 </div>

 <div class="simplesect" id="persistent-connections.web">
  <h3 class="title">Webリクエスト</h3>
  <p class="simpara">
   Webサーバーを PHP と統合し、Webページを生成させる方法はふたつあります:
  </p>
  <p class="simpara">
   最初は、CGI &quot;ラッパー&quot;としてPHPを使用する方法です。このように実行した場合、
   PHP インタプリタのインスタンスは、Webサーバーに(PHPページに関する)
   ページがリクエストされる度に生成され、破棄されます。
   リクエスト毎に破棄されるために、(SQLデータベースサーバーへのリンクのような)
   必要な全てのリソースは破棄される際にクローズされます。この場合、
   持続的接続を使用することから得るものは何もありません。
   持続的接続は持続しないのです。
  </p>
  <p class="simpara">
   2番目は、最も一般的ですが、PHP を PHP-FPM か、
   マルチプロセスWebサーバー(現在はApacheのみ)のモジュールとして実行する方法です。
   マルチプロセスサーバーは、通常、
   実際にWebページの提供を行う複数のプロセス(子)を管理するプロセス(親)を有しています。
   リクエストがクライアントから来ると、
   親プロセスは、
   他のクライアントにすでに送信を行っていないクライアントの一つに渡します。
   このため、
   同じクライアントが2番目のリクエストをサーバーに送信した際に最初のではなく他の子プロセスにより送信が行われる可能性があります。
   持続的接続がオープンされると、
   同じ子プロセスが処理する後続のページは、
   SQL サーバーへの確立済みの接続を再利用することができます。
  </p>
  <blockquote class="note"><p><strong class="note">注意</strong>: 
   <p class="para">
    Webリクエスト で使用されているメソッドを確認するには、
    <span class="function"><a href="function.phpinfo.php" class="function">phpinfo()</a></span> の出力における &quot;Server API&quot; の値、
    または Webリクエスト から実行した <strong><code><a href="reserved.constants.php#constant.php-sapi">PHP_SAPI</a></code></strong>
    の値を確認してください。
   </p>
   <p class="para">
    サーバーAPI が &quot;Apache 2 Handler&quot; や &quot;FPM/FastCGI&quot; の場合、
    持続的接続は同じワーカーで処理されるリクエスト間で使われます。
    これら以外の値の場合、
    持続的接続はそれぞれのリクエストを処理したあとは持続しません。
   </p>
  </p></blockquote>
 </div>

 <div class="simplesect" id="persistent-connections.cli">
  <h3 class="title">コマンドラインのプロセス</h3>
  <p class="simpara">
   コマンドラインから実行する PHP は、
   スクリプトごとに新しいプロセスを使います。
   持続的接続は、コマンドラインから実行するスクリプト間では共有されません。
   よって、cron やコマンドからのような一時的なスクリプトから、
   持続的接続を使ってもなんの役にも立ちません。
   とはいっても、役に立つ場面はあるかもしれません。
   たとえば、多くのリクエストやタスクを処理し、
   それぞれが独自のデータベース接続を必要とする可能性のある、
   長時間実行のアプリケーションサーバーの場合が挙げられます。
  </p>
 </div>

 <div class="simplesect" id="persistent-connections.why">
  <h3 class="title">持続的接続をなぜ使うのか？</h3>
  <p class="simpara">
   持続的接続は、SQLサーバーへ接続するオーバーヘッドが大きい場合に有効です。
   このオーバーヘッドが大きいかどうかは、データベースの種類、
   Webサーバーと同じマシン上にあるかどうか、
   そのマシンの負荷など、様々な要因に依存します。
   接続のオーバーヘッドが大きい場合、
   持続的接続は著しく効果があります:
   子プロセスは、SQLサーバーへの接続が必要なページを処理するたびに接続するのではなく、
   動作中に一度だけ接続を行います。
   このことは、
   持続的接続をオープンした子プロセスがそれぞれ、
   サーバーへの接続を維持するということになります。
   例えば、
   20の異なった子プロセスがそれぞれSQLサーバーへの持続的接続を行うスクリプトを実行した場合、
   そのサーバーへの個別の接続が、各子プロセスから1つずつ、合計20本行われます。
  </p>
 </div>

 <div class="simplesect" id="persistent-connections.drawbacks.conn-limits">
  <h3 class="title">ありうる欠点: 接続数の上限</h3>
  <p class="simpara">
   しかし、データベースへの接続数を制限して使用している場合に、
   持続的な子プロセスの接続数がその数を超える場合は、
   持続的接続には気をつけたほうが良い欠点がいくつかあります。
   もしデータベースの同時接続数の制限が16だとして、
   サーバーに多くのアクセスがあったため、
   17個の子プロセスが接続しようとするとそのうちの一つは接続に失敗します。
   もしスクリプトに接続をシャットダウンしないようなバグ(例えば無限ループ)があると、
   16程度の同時接続しか許容しないデータベースはすぐにダメになってしまいます。
  </p>
  <p class="simpara">
   持続的接続は通常、特定の時点で開く接続数を増加させます。
   これはアイドル状態のワーカーが、
   以前処理したリクエスト用の接続を保持し続けるためです。
   リクエストの急増に対応するために多数のワーカーが起動された場合、
   それらのワーカーが開いた接続は、
   ワーカーが終了するかデータベースサーバーが接続を閉じるまで残ります。
  </p>
  <p class="simpara">
   データベースサーバーが許可する最大接続数が、
   Webリクエストワーカーの最大数(それに cronジョブや、
   管理接続などのその他の使用分を加えた数)よりも大きいことを確認してください。
  </p>
  <p class="simpara">
   放棄された接続や、アイドル状態の接続(タイムアウト)の処理方法について、
   データベースのドキュメントで確認してください。
   タイムアウトを長く設定すると、
   同時に開かれる持続的接続の数が大幅に増加する可能性があります。
  </p>
 </div>

 <div class="simplesect" id="persistent-connections.drawbacks.state">
  <h3 class="title">ありうる欠点: 接続の状態管理</h3>
  <p class="simpara">
   一部のデータベース拡張モジュールは、
   接続が再利用される際にクリーンアップを自動的に実行します。
   このクリーンアップのタスクを、
   アプリケーション開発者の裁量に委ねる拡張モジュールもあります。
   選択したデータベース拡張モジュールとアプリケーション設計によっては、
   スクリプト終了前に手動でのクリーンアップが必要になる場合があります。
   接続を予期しない状態に陥らせる可能性のある変更には以下が含まれます：
  </p>
  <ul class="simplelist">
   <li>データベースの選択 / デフォルトのデータベース</li>
   <li>テーブルロック</li>
   <li>未コミットのトランザクション</li>
   <li>一時テーブル</li>
   <li>プロファイリングのような、特定の設定や機能を有効にした接続</li>
  </ul>
  <p class="simpara">
   クリーンアップが行われていない、
   または閉じられていないテーブルロックやトランザクションは、
   他のクエリが無限にブロックされる原因となるほか、
   その後の接続の再利用によって予期しない変更が生じる可能性があります。
  </p>
  <p class="simpara">
   誤ったデータベースが選択されている場合、
   その後の接続の再利用ではクエリが期待通りに実行できなくなります
   (スキーマが十分に類似している場合、
   誤ったデータベース上でクエリが実行される可能性があります)。
  </p>
  <p class="simpara">
   一時テーブルがクリーンアップされない場合、
   後続のリクエストでは同じテーブルを再作成できません。
  </p>
  <p class="simpara">
   クリーンアップは、クラスのデストラクタまたは
   <span class="function"><a href="function.register-shutdown-function.php" class="function">register_shutdown_function()</a></span>を使用して実装できます。
   また、クリーンアップの機能を組み込んだ、
   専用の接続プールプロキシの使用も検討するとよいでしょう。
  </p>
 </div>

 <div class="simplesect" id="persistent-connections.final-words">
  <h3 class="title">おわりに</h3>
  <p class="simpara">
   既に述べた持続的接続の振る舞いと、あり得る欠点を考慮すると、
   持続的接続を使う場合は、必ず慎重に検討を行うべきです。
   持続的接続を使う場合は、
   アプリケーションを追加で変更し、
   データベースサーバーとWebサーバーおよび/またはPHP-FPMを慎重に設定すべきです。
  </p>
  <p class="simpara">
   サーバーへ接続するオーバーヘッドの原因を調査・修正する別の代替案
   (例：データベースサーバーへのDNS逆引きの無効化）や、
   専用の接続プールプロキシを検討してください。
  </p>
  <p class="simpara">
   高トラフィックのWeb APIについては、代替のランタイムや、
   長時間実行可能なアプリケーションサーバーの使用を検討してください。
  </p>
 </div>

 <div class="simplesect" id="persistent-connections.seealso">
  <h3 class="title">参考</h3>
  <ul class="simplelist">
   <li><span class="function"><a href="function.ibase-pconnect.php" class="function">ibase_pconnect()</a></span></li>
   <li><span class="function"><a href="function.oci-pconnect.php" class="function">oci_pconnect()</a></span></li>
   <li><span class="function"><a href="function.odbc-pconnect.php" class="function">odbc_pconnect()</a></span></li>
   <li><span class="function"><a href="function.pfsockopen.php" class="function">pfsockopen()</a></span></li>
   <li><span class="function"><a href="function.pg-connect.php" class="function">pg_connect()</a></span></li>
   <li><a href="mysqli.persistconns.php" class="link">MySQLi and Persistent Connections</a></li>
   <li><a href="pdo.connections.php" class="link">PDO Connection Management</a></li>
  </ul>
 </div>
</div>
<?php manual_footer($setup); ?>