システムが長期間稼働すると、常に何らかの問題やボトルネックが発生します。問題が発生するのは怖いことではありません。それは単に位置決めの問題にすぎません。 > 問題の分析 - > 解決策の提案 - > 実践 - > 結果のフィードバック - > 要約して最適化します。
以前に行った最適化の実践ですが、最近調べてみたところ、いくつかの一般的な最適化手法がまだ再利用できることがわかりました。システムを長期間実行すると、常に何らかの問題やボトルネックが発生します。問題が発生するのは怖いことではありません。問題を特定するだけです。> 問題を分析するだけです。 ; 解決策の提案 -> 実践 -> 結果のフィードバック -> 要約して最適化します。
問題の説明: システムは PHP5 + Zend フレームワークを使用して開発されており、データの規模とアクセス量が増加した後 (数千万)、アクセスのピーク時間中 (get 後など)、バックグラウンドの Apache サーバーの負荷が高すぎるように見えます。この期間中、特に金曜日)、マシンの CPU 負荷が 170 以上に急増します。CPU 負荷が高いと、それに応じてリクエストの処理が遅くなるため、この問題を早急に解決する必要があります。
問題分析: 数日間連続して観察と分析を行った後、CPU 使用率が 100% に達すると、システムの CPU 使用率が大きな割合を占め、ユーザーの CPU 使用率はそれほど高くありません。 haproxy と Squid キャッシュの CPU 負荷は非常に低く、memcached と Squid のヒット率は一般的に約 60% に達します。
バックエンドのアクセス ログを分析すると、リクエストされたユーザー エージェントの大部分が検索クローラーであることがわかりました。
同時に、Apache 上で xdebug を構成し、アイドル期間中にメイン ページで一連のパフォーマンス データを測定しました。測定されたデータは、kcachegrind を使用して分析されました (xdebug の構成方法は、soso で検索できます)。そして次のことを発見しました:パフォーマンス データは十分に安定しておらず、テスト データは同じリクエスト間で大きく異なります
遅いポイントが点在しています
Memcached へのアクセスは、ほとんどの場合、比較的遅い (100 ミリ秒以上)
解決策 上記の予備分析を通じて、既存の手順に一連の調整が徐々に加えられました。
最初に考慮すべきことは、フロントエンドの Squid キャッシュのヒット率を高め、それによって Squid を通過してバックエンドの Apache に到達するリクエストの数を減らす方法を見つけることができるかどうかです。
かなりの数のリクエストが Crawler から発信されていることを考慮して、以前は Squid キャッシュは言語 Cookie が設定されたリクエストのみをキャッシュし、Crawler からのリクエストには Cookie 情報がありませんでした。そこで、Crawler からのすべてのリクエストを zh_CN の言語にデフォルト設定し、その後、haproxy の設定を変更して、User-Agent を備えた共通の Crawler からのすべてのリクエストを Squid キャッシュに転送することを考えました。
PHP コードを変更し、一部のページのキャッシュ時間を長く設定します
上記の 2 つの手順の後、Apache に到達するリクエストの数は確かに減少しましたが、これでは過剰な CPU 負荷の問題はほとんど解決されないため、別の方法を探しました。
次に、xdebug プロファイリングを使用した結果によると、memcached との対話には長い時間がかかるため、memcached がリクエストに対してより速く応答して、各リクエストをより速く完了できるようにする方法を見つけられないかと思います。同時実行性が低下すること。
コード分析により、オンラインの memcached は poll() を使用しており、混雑時でも memcached の接続数は約 1,000 のままであり、memcached の CPU 使用率は約 30% であることがわかりました。明らかに、多数の同時接続を処理する場合、poll() メソッドは非常に非効率的です。そこで、epoll() を使用してリクエストを処理するように memcached を再コンパイルしました。epoll に置き換えた後、memcached の CPU 使用率は約 30% から約 3% に減少しました。これは 10 倍です。
また、memcached のヒット率はそれほど高くなく、スワップアウトされるアイテム数も比較的多いため、当初は手動でパーティション分割する予定でしたが、後で気づきました。 PHP の最新の memcache 拡張機能は、キャッシュ キーに従って自動的にパーティションをサポートし、プログラム コードを変更せずに新しい memcached インスタンスを追加できます (構成ファイルを変更する必要があります:-))。そこで、各 Apache の php memcache 拡張機能をアップグレードし、新しい memcached を設定ファイルに追加しました。これで memcached のコンテンツ パーティションが完成しました。変更後の効果はより顕著で、ページの読み込み時間は変更前に比べて大幅に短くなります。
この 2 つのステップの調整後、memcached の効率は以前よりも向上しましたが、Apache の負荷は依然として高いため、他の解決策を考えるしかありません。
さらに詳細な分析 前述したように、メイン システムの CPU 使用率が非常に高いため、その原因を見つけるには、カーネルを深く調べるしかありません:) ここから、strace の旅が始まります。 Nike の広告スローガンを言い換えると: Just strace it!
ピーク時に httpd プロセスで strace を実行しました。メソッドは次のとおりです
strace -p PID -c は概要を取得します
strace -p PID -o Output.log ファイルに書き込んでゆっくり勉強してください
strace -p PID -e trace=file ファイルシステム操作に関連する syscall のみを調べます
strace -p PID -elstat64,stat64,open,getcwd はこれらの syscall のみをトレースします
…
上記の strace 分析から、次の結論を導き出すことができます:
lstat64、stat64、open などのシステムコールがたくさんあります。
上記のシステムコールにはかなり時間がかかります、orz
システムコールの大部分は失敗します。実際には、失敗が繰り返されるケースです。
上記のデータにより、これらの無意味なシステムコールがどのようにして発生するのかという問題の方向性がわかりました。
分析後、PHP が特定のクラスをロードしたい場合、include_path で定義された一連のディレクトリ内でそのクラスに対応するファイルを検索し、見つかるまで各ディレクトリを試します。この方法は明らかに非効率的ですが、これを達成するためのより良い方法はあります。
は複数あります。require_once() を呼び出すときは、パラメータとして絶対パスを記述します (Zend Framework を作成する人は最初これを理解していませんでした。後で更新されました))
__autoload() を使用してクラスの遅延ロードを実行します。これは、使用される可能性のあるすべてのクラス ファイルを require_once するのではなく、本当に必要な場合にのみクラスをロードすることを意味します。
問題は見つかりましたが、解決すべき問題はまだあります。開発中にコード内での絶対パスの使用に注意してください。改善できる唯一のことは、これを遅延読み込みに変更することです。ただし、Zend Framework の多数の require_once は相対パスを使用しており、これが問題の原因です。この記事で取り上げているのは、過剰な問題の根本的な原因です。
さて、問題が見つかったので、解決しましょう。クラス -> ファイル パスの対応を自動的に生成するスクリプトを作成し、コード内のすべてのクラスと Zend Framework のすべてのクラスの間の対応ファイルを生成します。コードおよび Zend Framework ライブラリ内のすべての require_once をコメント アウトします。その後、本番稼働前に詳細なテストを実施します。結果は驚くべきもので、負荷は 3 未満に減少しました。問題は解決されました。
概要:
コードを書く人は誰でも、問題が発生する可能性のある場所には常に問題が存在することを知っています (たとえそれがまだ見つかっていないとしても) 根本原因を解決することが最善の方法ではありません。どのような問題が解決されたとしても、この解決策の考え方は、ツールの使い方を上手にすることです。はい、この件についてはこれで終わりです。