Как оптимизировать обработку больших данных на laravel?

Моя задача:
«Чтобы взять таблицу транзакций, сгруппировать строки по дате транзакции и рассчитать статусы. Для этих манипуляций будет сформирована статистика, которая будет отображаться на странице».

Это мой метод генерации этой статистики

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;
}

если количество строк транзакций ниже 100 000 — это все правильно. Но если число выше 150-200k — nginx выбрасывает 502 плохих шлюза. Что вы можете мне посоветовать? У меня нет опыта работы с большими данными. Может быть, мои препятствия имеют фундаментальную ошибку?

1

Решение

Большие данные никогда не бывает легким, но я бы предложил использовать Laravel chunk вместо get,

https://laravel.com/docs/5.1/eloquent (Ctrl + F «:: Chunk»)

Какие ::chunk делает это выбрать N строк, и позволяют вам обрабатывать их по крупицам. Это удобно тем, что позволяет передавать потоковые обновления в браузер, но при диапазоне результатов ~ 150 000 я бы посоветовал посмотреть, как перенести эту работу в фоновый процесс, а не обрабатывать ее по запросу.

3

Другие решения

Так. После нескольких дней изучения информации по этому вопросу я нашел только один правильный ответ:

НЕ использовать 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
1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector