Фрагмент Laravel, не уменьшающий размер памяти PHP

#php #laravel #heroku #eloquent #chunking

#php #laravel #heroku #красноречивый #фрагментация

Вопрос:

Я достигаю пределов памяти Heroku PHP (512 МБ) при попытке извлечь более 25 тыс. записей из моей базы данных MySQL с помощью Eloquent chunking. В двух словах, я определяю свой SQL-запрос, разбиваю его на фрагменты, а затем загружаю результаты в PhpSpreadsheet. Согласно документам, фрагментация должна уменьшить использование моей памяти, но на практике, похоже, этого не происходит.

Я делаю что-то явно неправильное в приведенном ниже коде? (Я думаю, что большая часть специфики моего запроса совершенно не имеет значения, поэтому извините за дополнительную прокрутку, но хотел включить ее на случай, если я делаю что-то не так в этом.)

         $query = Participant::query()
        ->join('events', 'participants.event_id', '=', 'events.id')
        ->where('events.is_archived', '=', 'false')
        ->leftJoin('users as cu', 'participants.created_by_id', '=', 'cu.id')
        ->leftJoin('users as su', 'participants.selected_by_id', '=', 'su.id')
        ->select(DB::raw(
            'events.title as event_title,' . // A
            'events.store_number as event_store_number,' . // B
            'events.workfront_id as event_workfront_id,' . // C
            'events.company as event_company,' . // D
            'events.business as event_business,' . // E
            'DATE_FORMAT(events.start_date, "%m/%d/%Y") as event_start_date,' . // F
            'DATE_FORMAT(events.end_date, "%m/%d/%Y") as event_end_date,' . // G
            'events.region as event_region,' . // H
            'events.state as event_state,' . // I
            'events.default_language as event_language,' . // J
            'events.description as event_description,' . // K
            'participants.first_name as participant_first_name,' . // L
            'participants.last_name as participant_last_name,' . // M
            'CASE WHEN participants.is_contact <> 0 THEN participants.email ELSE "NO CONTACT" END as participant_email,' . // N
            'CASE WHEN participants.is_contact <> 0 THEN participants.phone ELSE "NO CONTACT" END as participant_phone,' . // O
            'participants.zip_code as participant_zip_code,' . // P
            'participants.language as participant_language,' . // Q
            'CASE WHEN participants.is_customer <> 0 THEN "yes" ELSE "no" END as participant_is_customer,' . // R
            'participants.current_carrier as participant_current_carrier,' . // S
            'CASE WHEN participants.is_contact <> 0 THEN "yes" ELSE "no" END as participant_is_contact,' . // T
            'DATE_FORMAT(participants.created_at, "%m/%d/%Y") as participant_created_date,' . // U
            'cu.first_name as creating_user_first_name,' . // V
            'cu.last_name as creating_user_last_name,' . // W
            'cu.email as creating_user_email,' . // X
            'DATE_FORMAT(participants.selected_date, "%m/%d/%Y") as participant_selected_date,' . // Y
            'su.first_name as selecting_user_first_name,' . // Z
            'su.last_name as selecting_user_last_name,' . // AA
            'su.email as selecting_user_email,' . // AB
            'CASE WHEN events.has_surveys_enabled <> 0 THEN "yes" ELSE "no" END as has_surveys_enabled,' . // AC
            'CASE WHEN events.has_surveys_enabled <> 0 THEN "N/A" ELSE CONCAT("' . request()->getHost() . '/surveys/create?event_id=", events.id) END as survey_link' // AD
        ))
        ->orderBy('events.title')
        ->orderBy('participants.created_at')
    ;

    $spreadsheet = new Spreadsheet();

    $sheetRowIndex = 1;

    $sheet = $spreadsheet->getActiveSheet();

    $query->chunk(1000, function ($eventRecords) use (amp;$sheet, amp;$sheetRowIndex) {
        foreach ($eventRecords as $eventRecord) {
            $sheet->fromArray([
                $eventRecord->event_title,
                $eventRecord->event_store_number,
                $eventRecord->event_workfront_id,
                $eventRecord->event_company,
                $eventRecord->event_business,
                $eventRecord->event_start_date,
                $eventRecord->event_end_date,
                $eventRecord->event_region,
                $eventRecord->event_state,
                $eventRecord->event_language,
                $eventRecord->event_description,
                $eventRecord->participant_first_name,
                $eventRecord->participant_last_name,
                $eventRecord->participant_email,
                $eventRecord->participant_phone,
                $eventRecord->participant_zip_code,
                $eventRecord->participant_language,
                $eventRecord->participant_is_customer,
                $eventRecord->participant_current_carrier,
                $eventRecord->participant_is_contact,
                $eventRecord->participant_created_date,
                $eventRecord->creating_user_first_name,
                $eventRecord->creating_user_last_name,
                $eventRecord->creating_user_email,
                $eventRecord->participant_selected_date,
                $eventRecord->selecting_user_first_name,
                $eventRecord->selecting_user_last_name,
                $eventRecord->selecting_user_email
            ], null, 'A' . $sheetRowIndex);
            $sheetRowIndex  ;
        }
    });
 

Комментарии:

1. что происходит, когда вы уменьшаете размер фрагмента меньше 1000 ?

2. Я могу попробовать, но я не думаю, что это должно иметь огромное значение, исходя из моего понимания того, как работает фрагмент? Не может быть, чтобы размер одного фрагмента был близок к 512 МБ

3. ну, электронная таблица содержит каждую добавленную к ней строку, но это кажется большим объемом памяти

4. Верно, но теоретически электронная таблица должна просто хранить эти данные в виде строк, которые довольно мало занимают память

5. Вы можете попробовать функцию экономии памяти PhpSpreadsheet