PDOで永続的な接続を使用することの欠点は何ですか


181

PDOでは、PDO::ATTR_PERSISTENT属性を使用して接続を持続させることができます。PHPマニュアルによると-

永続的な接続はスクリプトの最後では閉じられませんが、キャッシュされ、別のスクリプトが同じ資格情報を使用して接続を要求すると再利用されます。永続的な接続キャッシュを使用すると、スクリプトがデータベースと通信する必要があるたびに新しい接続を確立するオーバーヘッドを回避できるため、Webアプリケーションが高速になります。

また、PDO ODBCドライバーの使用中は永続的な接続を使用しないことをお勧めします。これは、ODBC接続プーリングプロセスを妨げる可能性があるためです。

したがって、最後のケースを除いて、PDOで永続的な接続を使用することには欠点がないようです。ただし、このメカニズムを使用することの他の欠点、つまり、このメカニズムによってパフォーマンスが低下するなどの状況があるかどうかを知りたいです。


うわー、あなたはこの簡単な質問に対して1000担当者の賞金を支払いましたか?
Pacerier

@Pacerier、いや、それは別の誰かでした。
Charles

回答:


287

ここで概説されている問題を軽減する方法の詳細については、以下の回答を必ずお読みください。


PDOを使用すると、永続的な接続を行う他のPHPデータベースインターフェースと同じ欠点が存在します。データベース操作の途中でスクリプトが予期せず終了した場合、残った接続を取得する次のリクエストは、デッドスクリプトが中断したところから再開されます。接続は、PHPレベルではなく、プロセスマネージャーレベル(Apache for mod_php、FastCGIを使用している場合は現在のFastCGIプロセスなど)で開いたままであり、PHPは親プロセスに接続の終了を通知しません。スクリプトは異常終了します。

デッドスクリプトがテーブルをロックした場合、それらのテーブルは、接続が停止するか、接続を取得する次のスクリプトがテーブル自体をロック解除するまでロックされたままになります。

デッドスクリプトがトランザクションの途中にあった場合、デッドロックタイマーが作動するまで多数のテーブルがブロックされる可能性があり、それでも、デッドロックタイマーは、問題の原因となっている古いリクエストではなく、新しいリクエストを強制終了できます。

デッドスクリプトがトランザクションの途中であった場合、その接続を取得する次のスクリプトもトランザクション状態を取得します。(アプリケーションの設計によっては)次のスクリプトが実際に既存のトランザクションをコミットしようとしない場合や、必要ないときにコミットしたり、必要ないときにロールバックしたりする可能性が非常に高くなります。

これは氷山の一角にすぎません。すべてのスクリプトリクエストでダーティ接続の後に常にクリーンアップを試行することにより、ある程度はすべて軽減できますが、データベースによっては、これが面倒な場合があります。あなたは、データベース接続の作成特定していない限りボトルネックになっている一つのこと、あなたのスクリプト内を(あなたが使用してプロファイリングコードやったこの手段Xdebugを、および/またはxhprofは)、あなたがすべきではない何の解決策として、持続的な接続を考慮してください。

さらに、ほとんどの最新のデータベース(PostgreSQLを含む)には、接続プールを実行する独自の方法があり、単純なPHPベースの永続的な接続のような直接的な欠点はありません。


ポイントを明確にするために、私たちは職場では持続的な接続を使用していますが、選択ではありません。奇妙な接続動作が発生しました。アプリケーションサーバーからデータベースサーバーへの最初の接続にちょうど 3秒かかっていました。カーネルのバグだと思います。ランダムに発生し、オンデマンドで再現できなかったため、トラブルシューティングの試行をあきらめました。アウトソーシングされたITには、それを追跡する具体的な能力がありませんでした。

いずれにせよ、倉庫のスタッフが数百個の入荷部品を処理していて、各部品が0.5秒ではなく3.5秒かかっている場合、彼らが私たち全員を誘拐して手助けする前に、私たちは行動を起こさなければなりませんでした。そのため、私たちは自社で開発したERP / CRM / CMSの怪物を少し変えて、永続的な接続のすべての恐怖を直接体験しました。一見ランダムに発生したすべての微妙な小さな問題と奇妙な行動を追跡するのに数週間かかりました。ユーザーがアプリから熱心に絞り出した週に1回の致命的なエラーは、ロックされたテーブル、放棄されたトランザクション、およびその他の不幸な不安定な状態を残していることがわかりました。

この悲惨な話にはポイントがあります。それは、パフォーマンスの名の下に、私たちが決して破ることを予期していなかったものを破りました。 トレードオフはそれだけの価値はなく、ユーザーからの暴動なしで通常の接続に切り替えることができる日を待ち望んでいます。


2
私は走る前にこの回答を読んだことを願っていますSELECT orders.* FROM orders LEFT JOIN items USING(item_id)
Ast Derek

31
10年近く持続的な接続を使用している大きなWebサイトを知っています。コツは、DB拡張の上のレイヤーを使用して、を使用してクリーンアップする必要があることを記憶させることですregister_shutdown_function()。プロセスが停止すると、接続も停止します。そうでない場合、接続はクリーンな状態にリセットされます(たとえば、開いているトランザクションはロールバックされます)。これが失敗した場合、接続は閉じられ、同じプロセスへの次の要求によって新しい接続が開かれます。永続的な接続を悪魔化する必要はありません。
Walter Tross 2013年

@Charlesに興味があります...あなたの問題は解決されましたか?
Tschallacka 2015

@MichaelDibbets数か月前にアプリケーションサーバーを交換し、pconnectをオフにして、3秒のバグがまだ残っているかどうかを確認しました。そうではなかった。プロキシによって解決されたと思います。以下に関する回答mysqli_change_userは、状態の問題を処理するように設計されていないアプリケーションで永続的な接続を行う必要がある人々にとって、おそらく最善の回避策です。
チャールズ

5
接続に5秒の遅延があり、これをDNS + IPv6の問題として切り分けました。サーバーはv6アドレスを探して失敗し、IPv4アドレスを使用していました。
Nigel Atkinson、

45

上記のチャールズの問題に対応して、

投稿者:http://www.php.net/manual/en/mysqli.quickstart.connections.php -

永続的な接続に関する一般的な不満は、再利用する前にそれらの状態がリセットされないことです。たとえば、未処理の未完了のトランザクションは自動的にロールバックされません。ただし、接続をプールに入れてから再利用するまでの間に行われた承認の変更は反映されません。これは、望ましくない副作用と見なされる場合があります。逆に、持続性という名前は、状態が持続するという約束として理解できます。

mysqli拡張機能は、持続的な接続の両方の解釈をサポートします。状態の持続と再利用前の状態のリセットです。デフォルトはリセットです。永続的な接続が再利用される前に、mysqli拡張機能が暗黙的に呼び出しmysqli_change_user()て状態をリセットします。永続的な接続は、開かれたばかりのように見えます。以前の使用からのアーティファクトは表示されません。

mysqli_change_user()機能は高価な操作です。最高のパフォーマンスを得るには、コンパイルフラグMYSQLI_NO_CHANGE_USER_ON_PCONNECTを設定して拡張機能を再コンパイルすることができます。

安全な動作と最高のパフォーマンスのどちらを選択するかはユーザーに任されています。どちらも有効な最適化目標です。使いやすさのために、最大のパフォーマンスを犠牲にして、安全な動作がデフォルトになっています。


+1、他の方法で混乱をクリーンアップしたという事実がなければ、手動でchange_user呼び出して奇妙な不明な状態の問題が修正されたかどうかを確認したいと思います。
Charles

PDO Postgresの永続的な接続に相当するものは何ですか?@Charlesのような同様の問題があります。しばらくすると、fetch sqlのようなエラーが発生します-サーバーが予期せず接続を閉じましたこれは、単純なSELECTクエリを実行すると(トランザクションではなく)サーバーが異常終了したことを意味します。
Carmageddon 2013

1
@Carmageddon、それは新しい質問により適していますが、tl; drはPostgresがpconnectを実行しないため、代わりに外部接続プールの1つを使用する必要があるということです。
チャールズ

@チャールズ、それはどういう意味ですか?PDOの永続的な接続を使用することは、「外部接続プール」を使用することと同等ではありませんか?またはどういう意味ですか?
Carmageddon 2013

@Carmageddon、私が言っていることは、Postgresコミュニティがpconnectよりも優れたソリューションとして接続プーリングに落ち着いたということです。pgbouncerまたはpgpool-IIをチェックしてください。とにかくPDOがPostgresをpconnectするかどうかはわかりませんが、完全にロッカーから外れているかもしれません。
Charles

13

永続的な接続は、データベースへの接続に(比較的)長い時間がかかる場合にのみ適しています。今日ではほとんどそうではありません。永続的な接続の最大の欠点は、サイトを閲覧できるユーザーの数が制限されることです。MySQLが一度に10の同時接続のみを許可するように構成されている場合、11人目がサイトを閲覧しようとしても機能しません。 。

PDOは永続性を管理しません。MySQLドライバーが行います。a)接続が利用可能で、ホスト/ユーザー/パスワード/データベースが一致する場合、接続を再利用します。変更がある場合、接続は再利用されません。最良の場合の正味の影響は、サイトにさまざまなユーザーがいて、それらを永続化しても何の効果もないため、これらの接続が頻繁に開始および停止されることです。

永続的な接続について理解する重要なことは、ほとんどのWebアプリケーションでそれらを使用してはならないということです。魅力的に聞こえますが、危険でほとんど役に立ちません。

これには他のスレッドもあると思いますが、永続的な接続はリクエスト間で持続するため危険です。たとえば、リクエスト中にテーブルをロックし、その後ロック解除に失敗した場合、そのテーブルは無期限にロックされたままになります。同じ接続が異なるリクエスト間で使用されるかどうかを知る方法がないため、永続的な接続はアプリの99%でもほとんど役に立ちません。各Webスレッドには独自の永続的な接続のセットがあり、どのスレッドがどの要求を処理するかを制御する方法はありません。

PHPの手続き型mysqlライブラリには、mysql_connectへの後続の呼び出しが、別の接続を開くのではなく(同じように)同じリンクを返す機能があります(ご想像のとおり)。これは永続的な接続とは関係がなく、mysqlライブラリに固有です。PDOはそのような動作を示しません


リソースリンク:リンク

一般的には、これを大まかな「ルールセット」として使用できます。

はい、永続的な接続を使用します。

  • 同じホスト上で200人の異なるユーザーが共有されているため、データベースにアクセスするアプリケーション/ユーザーはほとんどありません。つまり、200のオープン(ただしアイドル)接続はありません。
  • データベースは、ネットワーク経由でアクセスしている別のサーバーで実行されています

  • (1つの)アプリケーションがデータベースに頻繁にアクセスする

いいえ、次の場合は永続的な接続を使用しません。

  • アプリケーションは、1時間に100回だけデータベースにアクセスする必要があります。

  • 1つのデータベースサーバーにアクセスする多数のWebサーバーがあります。

特にネットワーク経由でデータベースにアクセスしている場合は、永続的な接続を使用するとかなり高速になります。データベースが同じマシンで実行されている場合、それほど大きな違いはありませんが、それでも少し高速です。ただし、名前が示すように、接続は永続的です。つまり、使用されていなくても、接続は開いたままです。

これに関する問題は、「デフォルトの構成」では、MySQLは1000の並列「オープンチャネル」しか許可しないことです。その後、新しい接続は拒否されます(この設定は微調整できます)。たとえば、クライアントが100台あるWebサーバーが20台あり、それぞれに1時間あたり1ページのアクセスしかない場合、簡単な計算では、データベースへの2000の並列接続が必要であることがわかります。それはうまくいきません。

Ergo:リクエストの多いアプリケーションにのみ使用してください。



1
「はい、次の場合に永続的な接続を使用します:[...]データベースにアクセスするアプリケーション/ユーザーがほとんどない場合」は、「要求が多いアプリケーションにのみ使用する」と矛盾します。ただし、後者は正しいです。状況:1秒あたり数千のリクエストにより、数百のアクティブなデータベース接続が発生します。システムが線形にスケーリングすると、データベースへの接続数も線形にスケーリングします。したがって、リクエストが増えると(ユーザーが増えると)、接続が増えることになります。あなたはとても必要な(!)あなたが要求(ユーザ)の多くを持っている限られ、まだ多くのアクティブな接続
ケン・ヴァンHoeylandt

12

私のテストでは、ローカルホストへの接続時間が1秒を超えていたため、永続的な接続を使用する必要があると想定しました。さらなるテストにより、「localhost」に問題があることがわかりました。

秒単位のテスト結果(php microtimeで測定):

  • ホストされているウェブ:connectDB:0.0038912296295166
  • localhost:connectDB:1.0214691162109(1秒以上:localhostを使用しないでください!)
  • 127.0.0.1:connectDB:0.00097203254699707

興味深いことに、次のコードは127.0.0.1を使用するのと同じくらい高速です。

$host = gethostbyname('localhost');
// echo "<p>$host</p>";
$db = new PDO("mysql:host=$host;dbname=" . DATABASE . ';charset=utf8', $username, $password,
    array(PDO::ATTR_EMULATE_PREPARES => false,
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

PDOはドメイン名の翻訳が難しいようです!ありがとう、クアッドコアマシンで各接続に長い時間がかかるのはなぜだろうと思っていました。
Mustafa

@ガンナーバーンスタイン+1素敵な発見。「localhost」は確かに時間がかかります。これにより、私のWebアプリの速度が多少向上しました(多くの接続が作成されます)。
imperium2335 2014

1
これは素晴らしい。開発マシンの解像度に問題があります... IPを使用すると、スクリプトが6.1秒から1.1秒になりました
Pete

localhostソケット接続を使用しており、ソケット接続は大量の接続が悪いことで有名です
mente

@menteその事実を証明できる参考資料、リソース?私はUDSがTCPよりも好まれていると思う傾向があります。ありがとう。
Nuxwin 2017

6

永続的な接続により、パフォーマンスが大幅に向上するはずです。私は、あなたがしつこさを「避ける」べきだという主張に同意しません。

上記の苦情は、誰かがMyIASMテーブルを使用し、テーブルロックを取得することにより、独自のバージョンのトランザクションをハッキングすることによって引き起こされているようです。PDOのbeginTransaction()を使用して、テーブルをInnoDBに移動します。


2
1年遅れて気づきましたが、記録のためです。私の話は、フルテキストインデックス作成サポートのためにMyISAMの泥沼に詰まった少数の非正規化クローンを除いて、完全にInnoDBテーブルで構成さたデータベースからのものです。
Charles

ちなみに、Sphinxは古くて破壊されており、ElasticSearchが新しい注目を集めています。ある晴れた日には、実際に新しいアプリではなく古いアプリにそれを使用します...
Charles

PostgreSQLでの全文検索が真の勝者です。すごい。別のツール/サーバーを実行する必要はありません。データの同期を保つことを心配する必要はありません。非常に詳細なコントロール。複数の辞書を使用するか、独自の辞書を作成します。また、PostgreSQLは自動的にマルチインデックスクエリを使用するため、実行中の他のクエリと組み合わせて使用​​することができます。
ブライトボール2014

2
MySQL 5.6は、InnoDBテーブルのフルテキストサポートを提供します。
timetofly 2014年

2

永続的な接続があると、より多くのシステムリソースを消費します。わずかな量かもしれませんが、それでも...


多くの場合、人間の時間とマイクロ秒のコンピュータ時間のトレード
Andy Chase

1

他のデータベースと比較してMySQLの方がはるかに高速であるにもかかわらず、永続的な接続を使用するための説明は、明らかにコストのかかる接続の数を減らしています。

持続的な接続に関する最初の問題...

1秒あたり1000の接続を作成している場合、通常は非常に長い間開いたままになっていることは保証されませんが、オペレーションシステムはそうです。TCP / IPプロトコルに基づいているため、ポートをすぐにリサイクルすることはできません。また、リサイクルされる前に「FIN」段階にしばらく投資する必要があります。

2番目の問題...多くのMySQLサーバー接続を使用しています。

* max_connections *変数を増やし、MySQLで100を超える同時接続を取得できることに多くの人が気づいていません。MySQLで1024を超える接続を伝達できないという古いLinuxの問題に打ち負かされた人もいます。

永続的な接続がmysqli拡張機能で無効にされた理由について、今すぐ話すことができます。永続的な接続を誤用してパフォーマンスが低下する可能性があるという事実にもかかわらず、これは主な理由ではありませんでした。実際の理由は–あなたはそれでもっと多くの問題を得ることができます。

MySQLがそれほど難しくなく、問題なく簡単に接続をリサイクルできた場合、MySQL 3.22 / 3.23の期間を通じて永続的な接続がPHPに入れられました。しかし、後のバージョンでは大量の問題が発生しました–コミットされていないトランザクションのある接続をリサイクルする場合、問題が発生します。カスタム文字セット構成で接続をリサイクルすると、セッション変数ごとに変換される可能性があるだけでなく、再び危険にさらされます。

永続的な接続を使用する際の1つの問題は、実際に適切にスケーリングされないことです。5000人のユーザーが接続している場合は、5000の永続的な接続が必要です。永続性の要件を回避するために、同じ数の接続を持つ10000人にサービスを提供できる場合があります。これは、接続していないときに個々の接続を共有できる立場にあるためです。


0

私は、部分的な解決策が1回限りの接続のプールを持つことになるのかと思っていました。システムの使用率が限界に達すると、接続プールの作成に時間を費やし、それらを配布して、完了またはタイムアウトしたときに強制終了することができます。バックグラウンドで、新しい接続が作成されているときに作成しています。最悪の場合、リンクの確立が制限要因であると想定して、プールなしで接続を作成するのと同じくらい遅くなるはずです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.