Bardzo dużo operacji na bazie w pętli + transakcja MySQL

Cześć,

mam za zadanie wykonywać około 25 save 13 sprawdzeń czy dany rekord już istnieje każda operacja z użyciem ActiveRecord. Ogólnie jeżeli chociaż jeden save się nie wykona to zrobi mi się bałagan, więc wrzuciłem to w transakcje MySQL i wszystko fajnie działa do około 1800 pętli, potem zaczyna się wyciek do pamięci z 14MB robi się nagle 50 i 100MB aż się zawiesza. Pętla średnio wychodzi 32tyś.




            try {


            $transaction = \Yii::$app->db->beginTransaction();


            foreach ($service->download($type) as $output) {// zwraca generator php

       

                // save, exists.......

                echo ++$i . ' ' . memory_get_peak_usage() . "\n"; // do 1800 pętli stabilnie 14MB, potem każde następne + 40MB aż się zawiesza

            }

       

            $transaction->commit();


        } catch (Exception $ex) {

 

            $transaction->rollBack();


            echo $ex->getFile() . ' ' . $ex->getMessage() . ' ' . $ex->getLine();

        }




Jak usunę transakcje pętla przechodzi bez problemu, próbowałem robić commit wraz z powtórnym rozpoczęciem transakcji po każdym przejściu pętli ale efekt był podobny. Coś robię nie tak…

Jakiego typu są te akcje w // save, exists… ? Nie dałoby się tego zrobić za pomocą jednej metody typu updateAll() albo batchInsert()?

17 tabele MySQL bierze udział w save oraz sprawdzaniu czy już dany rekord istnieje, po każdej pętli muszę sprawdzić czy w poprzedniej nie dodał już x rekordu w 7 tabelach.

Mógłbym podzielić operacje na mniejsze kawałki do 1000 pętli, tylko że to sporo pracy…

Sprawdzałem i na pewno problemem są operacje na bazie danych z transakcją ponieważ puszczając pustą pętle skrypt bierze jakieś 3,5MB

Aby oszczędzić pamięć możesz spróbować przejść z operacji na AR na zwykłe kwerendy. Jeśli musisz jednak operować na AR to w miejscach, w których iterujesz po ich liście używaj np. each() zamiast pobierać od razu wszystko all() i dopiero iterować.

W kwestii szybkości sprawdź czy baza jest dobrze zindeksowana. Jeśli wszystko zawiedzie zawsze możesz przerobić to na jakiegoś workera i odpalać w paczkach np. cronem dopóki się nie skończy.

Tyle mogę poradzić bez kodu.