ライブサーバーでのPHPスクリプトのプロファイリング

こんにちは、コミュニティ!



きっとあなたの1人がそのような問題に遭遇しました:サイトはゆっくり実サーバーで動作しています。

どの場所に問題があるかをすぐに見つけることが重要です。 これにxdebugを使用することはできません。サーバーに大きな負荷がかかり、測定自体にエラーが発生するためです。 この問題を解決するために、サイト上のツリーのような統計を迅速に収集できるシステム、pinbaを選択しました。



ハバですでにピンバとの仕事の詳細について話しました 。 読んでいない場合は、リンクを読むことができます。



せっかちな人のために、すぐに結果へのリンクを提供します。

Plus1 WapStartは通常、インスタンスごとに1秒あたり1000リクエストを超える負荷で実行されます。



どのように機能しますか?



データ収集


ピンバは、サーバーに(UDPを介して、非常に迅速に)時間間隔の開始マークと終了マーク(以降タイマーと呼びます)を送信し、MySQLテーブルにデータを格納します(読みやすい)。 例えば

$timer = pinba_timer_start(array('tag' => 'some_logic')); .... pinba_timer_stop($timer);
      
      





ツリー構造を構築するには、tree_id(毎回一意のID)とtree_parent_idの2つの追加タグを追加します。これは、現在のタグが埋め込まれているタイマーのtree_idです。 例えば



 $parent_timer = pinba_timer_start(array('tag' =>'some_logic', 'tree_id' => 1, 'tree_parent_id' => 'root')); $child_timer = pinba_timer_start(array('tag' =>'child_logic', 'tree_id' => 2, 'tree_parent_id' => 1)); pinba_timer_stop($child_timer); pinba_timer_stop($parent_timer);
      
      





したがって、サーバー上で、タイマーのネストを再現し、読み取り可能なツリーを構築できます。

時間を検出するために、プロジェクトのすべての興味深い場所にタイマーを配置しました(たとえば、SQLクエリ、ファイルへの書き込み時など)。



データ準備


残念ながら、pinbaは独自のpinba ENGINEを使用するため、クエリのインデックスを使用しません(PRIMARYを除く)(テーブルは実際にメモリに保存され、N分より古いデータは削除されます-この例では5分)。 しかし、ピンバはインデックスクエリを対象としていないため、文句を言うことはできません。

私たちにとって、インデックスは重要です。すべてのデータをpinbaテーブルから通常のMyISAMテーブルにコピーするからです。



 truncate table pinba_cache.request; truncate table pinba_cache.tag; truncate table pinba_cache.timer; truncate table pinba_cache.timertag; insert ignore into pinba_cache.request select * from pinba.request; insert ignore into pinba_cache.tag select * from pinba.tag; insert ignore into pinba_cache.timer select * from pinba.timer; insert ignore into pinba_cache.timertag select * from pinba.timertag;
      
      





リクエストからわかるように、システムはpinbaデータベースで動作し、pinba_cacheデータベースでコピーが動作します。



また、作業のために、tree_idフィールドとtree_parent_idフィールドがあるテーブルがもう1つ必要です。



 truncate table pinba_cache.timer_tag_tree; insert ignore into pinba_cache.timer_tag_tree SELECT * FROM ( SELECT null, timer_id, request_id, hit_count, timer.value, GROUP_CONCAT(timertag.value) as tags , (select timertag.value from pinba_cache.timertag where timertag.timer_id=timer.id and tag_id = (select id from pinba_cache.tag where name='treeId')) as tree_id , (select timertag.value from pinba_cache.timertag where timertag.timer_id=timer.id and tag_id = (select id from pinba_cache.tag where name='treeParentId')) as tree_parent_id FROM pinba_cache.timertag force index (timer_id) LEFT JOIN pinba_cache.timer ON timertag.timer_id=timer.id where not tag_id in ((select id from pinba_cache.tag where name='treeId'), (select id from pinba_cache.tag where name='treeParentId')) group by timertag.timer_id order by timer_id ) as tmp GROUP BY tree_id;
      
      





timer_tag_treeテーブルの構造を以下に示します。 残りのテーブルの構造は、pinbaと同じです。



 CREATE TABLE `timer_tag_tree` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `timer_id` INT(10) NOT NULL DEFAULT '0', `request_id` INT(10) NULL DEFAULT NULL, `hit_count` INT(10) NULL DEFAULT NULL, `value` FLOAT NULL DEFAULT NULL, `tags` VARCHAR(128) NULL DEFAULT NULL, `tree_id` VARCHAR(35) NOT NULL DEFAULT '', `tree_parent_id` VARCHAR(35) NOT NULL DEFAULT '', PRIMARY KEY (`id`), INDEX `timer_id` (`timer_id`), INDEX `tree_id_tree_parent_id` (`tree_id`, `tree_parent_id`), INDEX `tree_parent_id_tree_id` (`tree_parent_id`, `tree_id`) ) COLLATE='utf8_general_ci' ENGINE=MyISAM
      
      





データ分析


今-最も興味深い。 データを収集し、その後の作業に必要に応じてまとめました。 次に、このすべてのデータを便利な方法で生成するスクリプトを作成する必要があります。

1つのツリーを(1つの要求からサイトに)出力する方法-これは簡単なタスクなので、書きません。

問題は、ボトルネックを評価するために、それぞれが独自の関数呼び出し(タイマー)のツリーを持っている数百のphpリクエストを分析する必要があることです。 これらのツリーから1つの一般化されたツリーを収集する必要があります。



ユニオンアルゴリズムは次のとおりです。



各ノードについて、すべてのツリーに対するこのノードの実行時間の合計を考慮します。

2つのツリーを結合する関数を記述することにより、すべてを循環して合計を取得できます。

しかし、ここで私たちは不快な驚きを待っています-遅い時間。

写真からわかるように、2つのツリーを結合する難しさはO(N * N)です(N * log(N)でこれを行うことができますが、3行でより簡単な最適化方法があります)。 -ツリーにノードがありません。 したがって、小さな木を組み合わせると非常に不利なことに大きな木が有利です。

この機能を使用しようとします。 1つのスクリプトの実行ツリーをレベル1ツリーとして定義してみましょう。最初のレベルの2つのツリーの合計は2番目のレベルのツリーなどです。 そのような用語では、最初のレベルのツリーと、最小限の大きなレベルのツリーをさらに組み合わせる必要があります。 このようにします:



ご覧のとおり、関連付けの総数はN-1で、そのうちN / 2-最初のレベル、N / 4-2番目のレベル、N / 8-3番目などです。

このトリックは、再帰の助けを借りて非常に簡単に実装されます(必要に応じて、ループに展開できますが、わかりやすくするため、これは行いません)。



 //    ,   -   function mergeTreeList(array $treeList) { if (count($treeList) > 2) { return mergeTreeList( ($treeList), _($treeList)); } //... //    }
      
      





したがって、最初に元のツリーを2倍に組み合わせてから、それらを組み合わせてより大きなツリーにします。 私たちの時間の増加は約10倍(1000本の木)でした。



合計







落とし穴と短所







便利なファイル:

ツリーを表示するスクリプト: index.php

cron.sqlデータを変換するMySQLスクリプト

PinbaClient.class.php -tree_id、tree_parent_idの自動追加でより便利に使用するためのpinbaのラッパー

また、ネイティブのピンバをサポートしているonphpフレームワークについても言及したいと思います。

https://github.com/ents/pinba-php-profiler/-自宅ですべてを育てるソースファイル

http://pinba.org/-ここからピンバをダウンロードできます





免責事項:この記事は本質的に人気があり、行動へのガイドとして解釈することはできません。 以下で説明するすべてのアクションは究極の真実ではなく、ピンバから視覚化された情報を作成する数少ない方法の1つです。



All Articles