2016-04-29 17 views
0

誰もが! 私はいくつか問題があります。私の仕事は: "トランザクションテーブルを取るには、取引日ごとにグループ化された行とステータスを計算します。この操作は、ページ上にレンダリングされる統計情報を形成します"。laravelで大きなデータのhadleを最適化するには?

10万下の取引行のカウント場合、これは、この統計情報生成

public static function getStatistics(Website $website = null) 
    { 
     if($website == null) return []; 

     $query = \DB::table('transactions')->where("website_id", $website->id)->orderBy("dt", "desc")->get(); 

     $transitions = collect(static::convertDate($query))->groupBy("dt"); 
     $statistics = collect(); 

     dd($transitions); 

     foreach ($transitions as $date => $trans) { 
      $subscriptions = $trans->where("status", 'subscribe')->count(); 
      $unsubscriptions = $trans->where("status", 'unsubscribe')->count(); 
      $prolongations = $trans->where("status", 'rebilling')->count(); 
      $redirections = $trans->where("status", 'redirect_to_lp')->count(); 
      $conversion = $redirections == 0 ? 0 : ((float) ($subscriptions/$redirections)); 
      $earnings = $trans->sum("pay"); 

      $statistics->push((object)[ 
       "date" => $date, 
       "subscriptions" => $subscriptions, 
       'unsubscriptions' => $unsubscriptions, 
       'prolongations' => $prolongations, 
       'redirections' => $redirections, 
       'conversion' => round($conversion, 2), 
       'earnings' => $earnings, 
      ]); 

     } 

     return $statistics; 
    } 

の私の方法である - それは、すべてのライトの。しかし、カウントが150〜200kを上回っているなら - nginxは502の悪いゲートウェイを投げます。私に何をアドバイスできますか?私はbigdataの扱いにどんなexpierinceも持っていません。おそらく、私の印象に根本的な誤りがありますか?

ありがとうございました。

答えて

1

So.この質問に関する情報を数日間学んだ後、私はただ1つのライトの答えを見つけます:

生データを処理するためにPHPを使用しないでください。 SQLを使う方が良いです!

私の場合は、PostgreSQLを使用しています。

以下では、私を助けるSQLクエリを書きますが、他の誰かに役立つかもしれません。

WITH 
     cte_range(dt) AS 
     (
      SELECT 
       generate_series('2016-04-01 00:00:00'::timestamp with time zone, '{$date} 00:00:00'::timestamp with time zone, INTERVAL '1 day') 
     ), 

     cte_data AS 
     (
      SELECT 
       date_trunc('day', dt) AS dt, 
       COUNT(*) FILTER (WHERE status = 'subscribe') AS count_subscribes, 
       COUNT(*) FILTER (WHERE status = 'unsubscribe') AS count_unsubscribes, 
       COUNT(*) FILTER (WHERE status = 'rebilling') AS count_rebillings, 
       COUNT(*) FILTER (WHERE status = 'redirect_to_lp') AS count_redirects_to_lp, 
       SUM(pay) AS earnings, 
       CASE 
        WHEN COUNT(*) FILTER (WHERE status = 'redirect_to_lp') > 0 THEN 100.0 * COUNT(*) FILTER (WHERE status = 'subscribe')::float/COUNT(*) FILTER (WHERE status = 'redirect_to_lp')::float 
        ELSE 0 
       END 
       AS conversion_percent 

      FROM 
       transactions 

      WHERE 
       website_id = {$website->id} 

      GROUP BY 
       date_trunc('day', dt) 
     ) 

     SELECT 
      to_char(cte_range.dt, 'YYYY-MM-DD') AS day, 
      COALESCE(cte_data.count_subscribes, 0) AS count_subscribe, 
      COALESCE(cte_data.count_unsubscribes, 0) AS count_unsubscribes, 
      COALESCE(cte_data.count_rebillings, 0) AS count_rebillings, 
      COALESCE(cte_data.count_redirects_to_lp, 0) AS count_redirects_to_lp, 
      COALESCE(cte_data.conversion_percent, 0) AS conversion_percent, 
      COALESCE(cte_data.earnings, 0) AS earnings 

     FROM 
      cte_range 

     LEFT JOIN 
      cte_data 
      ON cte_data.dt = cte_range.dt 

     ORDER BY 
      cte_range.dt DESC 
2

大きなデータは決して簡単ではありませんが、getの代わりにLaravel chunkを使用することをおすすめします。

https://laravel.com/docs/5.1/eloquent(CTRL + F "::チャンク")

::chunkが一度に選択N行で、あなたは少しによってそれらを少し加工することができます。これはブラウザに更新をストリーミングすることができるという点で便利ですが、〜150kの結果範囲では、この作業をリクエストに応じて処理するのではなく、バックグラウンドプロセスにプッシュする方法を調べることをお勧めします。

+0

ありがとうございます。この処理をバックグラウンドプロセスに組み込む方法を詳しく教えてください。 Ajaxで? – Scrobot

+0

このロジックをジョブに移動し、要求に対してロジックを実行する代わりにジョブをキューに入れます。さらに詳しい情報:https://laravel.com/docs/5.1/queues – Josh

+0

そこから、ジョブのステータスをチェックし、完全な更新の割合を返したり、進行状況をファイルに書き込んだりするだけで "まだビジーな"ブール値を返すコントローラを持つことができますまたはチャンクの間にmemcachedします。 – Josh

関連する問題