最近私はゲームデータ統計の背景に取り組んでいます。最も基本的な機能は、登録ログとログインログを分析してユーザーデータを表示することです。社内テストではユーザー数が非常に少なかったため、パフォーマンスの問題は見つかりませんでした。しかし、この 2 日間は実際のテスト環境に置かれ、午後から大量のユーザーが殺到し、オンラインユーザーの統計が滞り始め、クエリ速度が返されるまでに数秒かかりました。登録人数は大丈夫でした。夜になるとオンライン人口の統計が基本的にタイムアウトして開けなくなります。ゲーム側にどのようなバグがあるのかはわかりませんが、プレイヤーはログインの問題に頻繁に遭遇し、その結果、オンラインおよび登録者数が少なくなります。しかし、この量のデータではクエリ速度が十分ではなく、非常に恥ずかしいことです。
現在、彼らはゲームのバグをチェックしており、私は統計の背後にあるコードのパフォーマンスも調べています。まず最初に、私が統計に使用するデータはスレーブ データベースからのものであり、彼らのゲームはメイン データベースを使用していることを説明します。また、ここには管理者が数人しかいないため、ゲーム サーバーのパフォーマンスに影響を与えることは不可能です。 。
今日、プロジェクト チームのリーダーはすべてのデータベースを会社のサーバーにインポートしました。統計プラットフォームのパフォーマンスの問題がどこにあるかを確認するために、コピーをローカル マシンにコピーしました。その後、登録統計さえも非常に遅く、サーバーでは返されるまでに約 2 秒かかり、ローカル マシンでは 20 秒以上かかり、頻繁にタイムアウトになることがわかりました (PHP のデフォルト設定は 30 秒のタイムアウトです)。言うまでもなく、オンライン統計は開くことができません。データベースを調べたところ、その日の登録レコードは約 3,500 件しかありませんでした (偽のデータあり)。これらは 5 分ごとにカウントされており、これは 1 日あたり 288 回です。もちろん、これはデータベースを 288 回ループするような、死ぬほど叱られるようなクエリではありません。
期間内の登録数をカウントするロジックも非常に単純で、各期間でデータを 1 回走査し、時間サイズを比較し、一致する場合は +1 します。しかし、たった 100 万ループのこのような単純なロジックに 30 分もかかるのはなぜでしょうか?
重要な問題は時間の比較にあり、タイムスタンプは時間サイズを比較するためのより科学的な方法であることは誰もが知っており、データベースに記録される時間は通常、PHP の形式で YYYY-mm-dd HH:ii:ss になります。タイムスタンプに変換する strtotime 関数があります。ただし、288 for * 3500 foreach の祝福の後、ここでの実行時間は 30 分にもなります。
$nowDayDT = strtotime( date('Y-m-d') ); $__startT = microtime(TRUE); for($i=0; $i<$allTime; $i += $gapTime){ $count = 0; //用于数据比较的 $startDT = $nowDayDT+$i; $endDT = $nowDayDT+$i+$gapTime; //用于显示的 $xAxis1 = date('H:i', $nowDayDT+$i); $xAxis2 = date('H:i', $nowDayDT+$i+$gapTime); foreach($rawData as $line){ $time = strtotime($line['log_dt']); if( $startDT<=$time && $time<$endDT ){ $count ++; } } $resArr[] = [ 'date'=>$xAxis1.'~'.$xAxis2, 'number'=>$count ]; } echo microtime(TRUE)-$__startT;
この場合、基本的にこの strtotime 関数を使用する方法はもうありません。では、時間を比較する他の方法は何でしょうか?答えは単純ですが、PHP では 2 つの日付と時刻の文字列を直接比較できます。したがって、変更されたコードは次のようになります。現在の実行時間は約 0.3 秒です
トラバーサルと再最適化
for にネストされた foreach があるため、このパフォーマンスを完全にトラバースする必要があるかどうかは少し心配です。中のフォーリーチ?実際、その必要はありません。 SQLデータを確認する限り、時間順にソートされています。最適化された時間比較アルゴリズムは次のとおりです。
$__startT = microtime(TRUE); for($i=0; $i<$allTime; $i += $gapTime){ $count = 0; //用于数据比较的 $startDT = date('Y-m-d H:i:s', $nowDayDT+$i); $endDT = date('Y-m-d H:i:s', $nowDayDT+$i+$gapTime); //用于显示的 $xAxis1 = date('H:i', $nowDayDT+$i); $xAxis2 = date('H:i', $nowDayDT+$i+$gapTime); foreach($rawData as $line){ $time = $line['log_dt']; if( $startDT<=$time && $time<$endDT ){ $count ++; } } $resArr[] = [ 'date'=>$xAxis1.'~'.$xAxis2, 'number'=>$count ]; } echo microtime(TRUE)-$__startT;
ここでは、 continue キーワードと Break キーワードを巧みに使用して、ループをスキップし、ループ全体を終了します。今回は、一日の初めの時間統計において、その後のデータの大部分を直接スキップできます。最終的に、総移動時間は約 0.12 秒に短縮されました。
要約すると、大規模なデータ処理では、トラバーサル中のデータ変換を避け、複雑な原理を持つ一部の関数の使用を避ける必要があります。 strtotime
など