php5 + MySQLを200リクエスト/秒以上にスケールするには?


16

私はパフォーマンスのためにホームページを微調整しています。現在、3つのSQLクエリを処理する3.14.byでは毎秒約200リクエスト、php14フォーラムである3.14.by/forumでは毎秒20 reqを処理しています。

奇妙なことに、いくつかのVPSと専用のAtom 330サーバーで数値はほぼ同じです。

サーバーソフトウェアは次のとおりです。Apache2+ mod_php prefork 4子(ここで別の数字を試しました)、php5、APC、nginx、PHPセッションストレージ用のmemcached。

MySQLは使用可能なRAMの約30%を消費するように構成されています(VPSで約150 MB、専用サーバーで700 MB)

これはどこかにボトルネックがあるように見えますが、私がより高くなることを許可していませんか?(つまり、6個未満のSQLを実行すると高速化されることはわかっていますが、キャッシュされたクエリのためにsqldがトップの数%しか消費しないため、これは制限要因には見えません)

事前にフォークされたapache2をキックしてnginx + phpだけを残す方がずっと速いことをテストした人はいますか?

いくつかのベンチマーク

Small 40-byte static file: 1484 r/s via nginx+apache2, 2452 if we talk to apache2 directly. 
Small "Hello world" php script: 458 r/s via ngin+apache2.

更新: ボトルネックは、キャッシュされたデータのMySQLパフォーマンスにあるようです。単一のSQLを含むページには354req / secが表示され、6つのSQL-180 req / secが表示されます。ここで何を調整できると思いますか?(MySQLで100-200Mbを分岐できます)

[client]
port        = 3306
socket      = /var/run/mysqld/mysqld.sock

[mysqld_safe]
socket      = /var/run/mysqld/mysqld.sock
nice        = 0

[mysqld]
default-character-set=cp1251
collation-server=cp1251_general_cs

skip-character-set-client-handshake

user        = mysql
pid-file    = /var/run/mysqld/mysqld.pid
socket      = /var/run/mysqld/mysqld.sock
port        = 3306
basedir     = /usr
datadir     = /var/lib/mysql
tmpdir      = /tmp
skip-external-locking

bind-address        = 127.0.0.1

key_buffer      = 16M
max_allowed_packet  = 8M
thread_stack        = 64K
thread_cache_size   = 16
sort_buffer_size    = 8M
read_buffer_size    = 1M

myisam-recover      = BACKUP
max_connections        = 650
table_cache            = 256
thread_concurrency     = 10

query_cache_limit       = 1M
query_cache_size        = 16M

expire_logs_days    = 10
max_binlog_size         = 100M

[mysqldump]
quick
quote-names
max_allowed_packet  = 8M

[mysql]
[isamchk]
key_buffer      = 8M

!includedir /etc/mysql/conf.d/

なぜApacheとnginxの両方を使用しているのですか?
-jamieb

これは一般的な構成であり、Apache2からPHP、およびApacheインフラストラクチャを必要とするさまざまなアプリ、nginxはロード時のapache2メモリフットプリントを削減します。
BarsMonster

実際、私はあなたの問題を理解していません。あなたのサイトは現在遅いですか?もしそうなら、それはどれくらい遅いですか?そして、あなたはそれをどれくらいスピードアップしたいですか?サイトの一部のプロファイルを作成して、ボトルネックがどこにあるかを判別しようとしましたか?
-jamieb

説明の中にあります:今では1秒あたり180-200リクエストです。ホームページにはこれで十分ですが、このセットアップを微調整して、同じコードベースで構築された他のサイトをより速く動作させたいと思います。理想的には、動的ページで100Mbit接続を飽和させたい:-)
BarsMonster

2
このコンテキストでは、「1秒あたりのリクエスト数」は意味のあるメトリックではありません。私のネットブックは「毎秒200リクエスト」を処理できます。そのような接続レートで達成したい応答時間を教えてください。
-jamieb

回答:


29

明らかに、あなたが試すことができることがたくさんあります。最善の策は、インデックスを使用しないクエリ(それらのログを有効にする)やその他の最適化されていないクエリのログを追跡することです。私は長年にわたってパフォーマンス関連のオプションの膨大なリストを編集してきたので、ここにあなたの情報のために小さなサブセットを含めました-うまくいけばそれが役立つでしょう。試してみることのできる一般的な注意事項を以下に示します(まだ行っていない場合)。

MySQL

  • query_cache_type = 1-キャッシュSQLクエリはオンです。2に設定すると、クエリは、SQL_CACHEヒントが渡された場合にのみキャッシュされます。同様に、タイプ1では、SQL_NO_CACHEヒントを使用して特定のクエリのキャッシュを無効にできます。
  • key_buffer_size = 128M(デフォルト:8M)-MyISAMテーブルインデックスのメモリバッファー。専用サーバーでは、key_buffer_sizeをサーバーの合計メモリー量の少なくとも4分の1に設定しますが、半分以下に設定してください。
  • query_cache_size = 64M(デフォルト:0)-クエリキャッシュのサイズ
  • back_log = 100(デフォルト:50、最大:65535)-未処理の接続要求のキュー。短時間で多くの接続がある場合にのみ重要
  • join_buffer_size = 1M(デフォルト:131072)-全表スキャン(インデックスなし)を行うときに使用されるバッファー
  • table_cache = 2048(デフォルト:256)-max_user_connectionsに、最も重いSQLクエリに含まれるJOINの最大数を掛ける必要があります。ピーク時に「open_tables」変数をガイドとして使用してください。「opened_tables」変数も見てください-「open_tables」に近いはずです
  • query_prealloc_size = 32K(デフォルト:8K)-ステートメントの解析と実行のための永続メモリ。複雑なクエリがある場合は増やす
  • sort_buffer_size = 16M(デフォルト:2M)-ソートに役立ちます(ORDER BYおよびGROUP BY操作)
  • read_buffer_size = 2M(デフォルト:128K)-順次スキャンを支援します。連続スキャンが多い場合は増やします。
  • read_rnd_buffer_size = 4M-MyISAMテーブルがソート後の読み取りを高速化するのに役立ちます
  • max_length_for_sort_data-ソートファイルに行ポインターの代わりに保存する行サイズ。ランダムなテーブル読み取りを回避できます
  • key_cache_age_threshold = 3000(デフォルト:300)-キーキャッシュをホットゾーンに保持する時間(ウォームに降格される前)
  • key_cache_division_limit = 50(デフォルト:100)-より洗練されたキャッシュ削除メカニズムを有効にします(2レベル)。最下位レベルに維持する割合を示します。delay_key_write = ALL-インデックスが更新されるたびにテーブルのキーバッファはフラッシュされませんが、テーブルが閉じられたときだけです。これにより、キーへの書き込みが大幅に高速化されますが、この機能を使用する場合は、--myisam-recover = BACKUP、FORCEオプションでサーバーを起動して、すべてのMyISAMテーブルの自動チェックを追加する必要があります
  • memlock = 1-メモリ内のプロセスをロック(スワップイン/アウトを減らすため)

アパッチ

  • 生成方法を変更します(たとえば、mpmに)
  • 可能であればログを無効にする
  • AllowOverride None-可能な限り.htaccessを無効にします。.htaccessファイルが使用されていない場合、それを探すためにApacheを停止し、ファイルルックアップリクエストを保存します。
  • SendBufferSize-OSのデフォルトに設定します。混雑したネットワークでは、通常ダウンロードされる最大ファイルのサイズに近いこのパラメーターを設定する必要があります
  • KeepAlive Off(デフォルトはOn)-lingerdをインストールして、ネットワーク接続を適切に閉じます。
  • DirectoryIndex index.php-ファイルリストをできる限り短く絶対的に保ちます。
  • オプションFollowSymLinks-Apacheのファイルアクセスプロセスを簡素化する
  • mod_rewriteまたは少なくとも複雑な正規表現の使用を避ける
  • ServerToken = prod

PHP

  • variables_order = "GPCS"(環境変数が必要ない場合)
  • register_globals = Off-セキュリティリスクであることに加えて、パフォーマンスへの影響もあります
  • include_pathを可能な限り最小限に抑えます(余分なファイルシステム検索を回避します)
  • display_errors = Off-エラーの表示を無効にします。すべての本番サーバーに強くお勧めします(問題が発生した場合にerrorいエラーメッセージを表示しません)。
  • magic_quotes_gpc =オフ
  • magic_quotes _ * =オフ
  • output_buffering = On
  • 可能な場合、ログを無効にします
  • Exposure_php = Off
  • register_argc_argv = Off
  • always_populate_raw_post_data =オフ
  • php.iniファイルを、phpが最初に探す場所に配置します。
  • session.gc_divisor = 1000または10000
  • session.save_path = "N; / path"-大規模なサイトでは、使用を検討してください。セッションファイルをサブディレクトリに分割します

OSの調整

  • -o noatimeオプションを使用して、使用済みのハードディスクをマウントします(アクセス時間なし)。また、このオプションを/ etc / fstabファイルに追加します。
  • / proc / sys / vm / swappiness(0〜100)を微調整して、最良の結果が得られるものを確認します。
  • RAMディスクを使用-mount --bind -ttmpfs / tmp / tmp

これは素晴らしいリストです。これらのほとんどは既にあり、残りのものを追加してもパフォーマンスは向上していません。ボトルネックはPHPとMySQLの間のどこかで、クエリキャッシュからの1秒あたり800を超えるリクエストを処理できないようです...
BarsMonster

さて、どのようにデータベースに接続しますか(mysql_connect()ではなくmysql_pconnect())?永続的な接続を使用していますか?両方の方法を試してみてください...
Ivan Peevski

すでにpconnectを使用しており、php.iniで接続プーリングが有効になっています...:
BarsMonster

完全を期すために、ただ接続してみます。私は、(特に負荷テストで)パフォーマンスが向上するケースを見てきました。
イヴァンペエフスキ

1

ボトルネックがCPUではない場合、そのIO(ネットワークまたはディスク)。そのため、IOがどの程度進行しているかを確認する必要があります。私はそのネットワークを考えていなかったでしょう(10mbps半二重リンクを使用している場合を除きますが、自動検出が正しく機能しない場合はスイッチをチェックする価値があります)。

ディスクI / Oは残り、特にVPSで大きな要因になる可能性があります。sarまたはiostatを使用してディスクを確認し、ディスクが頻繁に使用されている場合に詳細を検索する方法をgoogleします。


はい、ネットワークは問題ではありません-ローカルサーバーからabを実行する場合、パフォーマンスはまったく同じです。iowait時間を確認しました-0,01%未満です-基本的にはすべてがディスクキャッシュにあり、要求の処理にディスク書き込みは含まれていません(すべてのログが無効になっています)。
BarsMonster

1

私はどちらかとキャッシングになりますnginxの memcached)またはVarnishの

少なくとも、SaveTheRbtzが言ったように、静的ファイルをNginxでサーバーする必要があります。


これらは動的ページであるため、キャッシュしないでください。
BarsMonster

1
memcachedは従来のキャッシングアプリではないため、動的なページで驚くほど機能します。DBとアプリの間に位置します。アプリは最初にmemcachedにオブジェクトを照会し、存在しない場合はDBからロードします。最終的な効果は、DB上の永続的なストレージがはるかに遅くなるのではなく、RAMを使用してDBリクエストを処理することです。
-jamieb

Memcacheは、既知の機能であるnginxで使用できます。低速の永続ストレージは使用されず、すべてMySQLのクエリキャッシュにあります。
-BarsMonster

MemcachedとMySQLのクエリキャッシュは実際には比較できません。彼らは同じことすらしません。ここに投稿された提案のほとんどすべてを、それらを理解することを気にせずに簡単に撃ち落とすことができます。私はあなたがもう少し心を開いていることをお勧めします。
-jamieb

memcachedとMySQLクエリキャッシュの違いを明確に理解しています。しかし、すべてがクエリキャッシュにあり、ヒット率が100%であるという事実のため、「低速永続ストレージ」とは呼びません。昨日の元の答えは、ページ全体をキャッシュする非常に一般的なシナリオであるNginX + Memcachedの使用に関するものでした。個々のオブジェクトをキャッシュすることは、まったく別のシナリオです。MySQLの前でmemcachedを使用することはテーブルにありますが、今は(かなりのコード変更が必要になるため)それなしでより多くのジュースを取得することを考えています。
BarsMonster

1

サーバーには問題がないように見えるので、おそらく負荷ジェネレーターに問題があります。複数のマシンで実行してみてください。


サーバー自体から実行してもパフォーマンスは同じです。同時接続の数に関係なく-10または50。負荷テストはab -c 10 -t 10を介して
-BarsMonster

1

Apacheが許可する最大接続数に達しているようです。Apacheの設定を確認してください。I / Oやメモリなどの他の制限にまだ縛られていない場合は、サーバーの制限と最大クライアントを増やすと役立ちます。mpm_prefork_moduleまたはmpm_worker_moduleに存在する値を確認し、必要に応じて調整します。

ServerLimit 512
MaxClients 512

まあ、私は本当に....これは私がより多くの物理コア* 2つのApache2のプロセスよりも持つのない多くのローミングサービスはありません信じて、私は、apache2の目の前にnginxのを持っていることを提供する必要がありますか
BarsMonster

これを確認しました。Apache2プロセスの数を4から16に増やしても、パフォーマンスはまったく向上しませんでした(0.5%低下しました)。nginxワーカーの数を2または4に増やしても何も改善されませんでした。
BarsMonster

1
データがかなり静的な場合、つまり、他のすべてのページの読み込みが更新されない場合は、query_cacheを増やすことができます。MySQLは結果セットをそのまま保持し、メモリからプルします。ただし、キャッシュされているテーブルがその間に書き込みを受信すると、キャッシュは無効になり(データが影響を受けていなくても)、メモリが無駄になります。
エリックギベルティ

現在、クエリキャッシュヒット率は100%であり、MySQLはまだ遅いと感じています
...-BarsMonster

1
MySQL構成ファイルにskip-name-resolveを追加します。これにより、サーバーへのすべての接続でDNSルックアップが保存されます。ここでの欠点は、すべての接続をIPでロックする必要があることです(「%」を使用しないと仮定)。SQLが同じサーバー上にあり、localhost以外の場所にアクセスする必要がない場合は、skip-networkingを追加してTCP / IPスタック全体を強制終了することもできます。ただし、ボトルネックはApacheにあると思います。
エリックギベルティ

0

この負荷はツールによって生成されたものですか、それとも実際の負荷ですか?

memcachedを確認することもできます。接続率が高いと、アプリケーションで遅延が発生する問題が発生しました。

ロードジェネレーターを使用している場合、小さな静的ページにアクセスすると何が得られますか?

ロード中に、ネットワークスタックのTIME_WAIT状態を確認することができます。おそらく、接続キューがいっぱいになっています。

あなたが見ることができる約100の理由と項目がありますが、これ以上の情報がなければ、私はこの時点で推測を捨てています。


サーバー自体からベンチマークしているab-c 10 -t 10 URLを介してテストされているため、ネットワークは問題になりません。リクエストごとにベンチマークを追加しました。
BarsMonster

私は、abを使ったチューニングにあまり労力を費やすことはありません。あなたはそれが現実世界のパフォーマンスにうまく変換しないことに気付くかもしれません。あなたがしたいことは、アプリを分析し、各コンポーネントをテストすることです。たとえば、非常に小さな静的ページだけでapacheサーバーに直接アクセスします。これにより、バックエンドでの最大要求/秒がわかります。nginxを前に置き、同じバックエンドファイルを呼び出して再テストします。次に、単純な「hello world」タイプのphpページでテストします。すべてのレイヤーが単純なものをマスクできる場合があります。また、テスト中に接続を監視します。ネットワークスタックがいっぱいになっていないことを確認してください。
jeffatrackaid

昨日これらのベンチマークを行いましたが、更新された元の質問の説明にあります。また、テストはローカルホストで行われるため、ネットワークは問題になりません。
-BarsMonster

ネットワークは、ローカルホストで行われた場合でも問題になる可能性があります。あなたのケースではそうではありませんが、問題を引き起こす可能性があります。少なくとも現在のPHPのセットアップでは、最大で450リクエスト/秒の上限があります。次のステップは、データベース呼び出しをドロップし、その変化を確認することです。高レベルのチューニングを行うときにこれをバラバラにすることは、ほとんどの問題の原因となっているレイヤーを正確に特定するのに役立ちます。
jeffatrackaid

-1

このような問題の99%がデータベースにまでさかのぼります。まず最初にヒットインデックスを確認してください。それでもうまくいかない場合は、できる限りすべてのキャッシュを開始します。


すべてのインデックスであり、私が言っていたように、ケースの100%でMySQLクエリキャッシュにヒットすることさえあります
-BarsMonster

-1

(可能であれば)接続プーラーを使用して、データベースをWebアプリケーションに接続したままにすることをお勧めします(要求ごとに再接続する必要はありません)。それは速度の大きな違いを生むことができます。

また、EXPLAINを使用してすべてのクエリを分析してみてください(SHOW PROFILEを使用してクエリをプロファイルしないのはなぜですか?)。


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