2013-04-19 11 views
5

Mam kilka tysięcy rekordów (przechowywanych w tabeli w tabeli MYSQL), które muszę "przetworzyć w partiach". Wszystkie rekordy zawierają duży JSON. W niektórych przypadkach JSON wynosi ponad 1 MB (tak, mój DB ma znacznie ponad 1 GB).Uruchamianie intensywnego procesu wsadowego w PHP i unikanie wyczerpywania pamięci

Mam funkcję przechwytującą rekord, dekoduje JSON, zmienia niektóre dane, ponownie koduje tablicę PHP z powrotem do JSON i zapisuje ją z powrotem do bazy danych. Dość proste. FWIW, jest to w kontekście aplikacji CakePHP.

Biorąc pod tablicą identyfikatorów, ja próbuje zrobić coś takiego (bardzo prostego kodu mock):

foreach ($ids as $id) { 
    $this->Model->id = $id; 
    $data = $this->Model->read(); 
    $newData = processData($data); 
    $this->Model->save($newData); 
} 

Problem jest to, że bardzo szybko, PHP zabraknie pamięci. Kiedy uruchamiasz foreach w ten sposób, to prawie tak, jakby PHP przenosiło się z jednego rekordu do drugiego, bez zwalniania pamięci wymaganej dla poprzednich operacji.

Czy istnieje jeszcze sposób uruchomienia pętli w taki sposób, aby pamięć została zwolniona przed przejściem do następnej iteracji pętli, tak aby faktycznie przetworzyć ogromną ilość danych?

Edytuj: Dodawanie więcej kodu. Ta funkcja pobiera mój JSON, konwertuje go do tablicy PHP, wykonuje pewne manipulacje (mianowicie rekonfigurację danych w oparciu o to, co jest obecne w innej tablicy) i zastępowanie wartości w oryginalnej tablicy. JSON ma wiele warstw głębokości, stąd niezwykle długie pętle foreach.

function processData($theData) { 
    $toConvert = json_decode($theData['Program']['data'], $assoc = true); 
    foreach($toConvert['cycles'] as $cycle => $val) { 
     foreach($toConvert['cycles'][$cycle]['days'] as $day => $val) { 
      foreach($toConvert['cycles'][$cycle]['days'][$day]['sections'] as $section => $val) { 
       foreach($toConvert['cycles'][$cycle]['days'][$day]['sections'] as $section => $val) { 
        foreach($toConvert['cycles'][$cycle]['days'][$day]['sections'][$section]['exercises'] as $exercise => $val) { 
         if (isset($toConvert['cycles'][$cycle]['days'][$day]['sections'][$section]['exercises'][$exercise]['selectedFolder'])) { 
          $folderName = $toConvert['cycles'][$cycle]['days'][$day]['sections'][$section]['exercises'][$exercise]['selectedFolder']['folderName']; 
          if (isset($newFolderList['Folders'][$folderName])) { 
           $toConvert['cycles'][$cycle]['days'][$day]['sections'][$section]['exercises'][$exercise]['selectedFolder'] = $newFolderList['Folders'][$folderName]['id']; 
          } 
         } 
         if (isset($toConvert['cycles'][$cycle]['days'][$day]['sections'][$section]['exercises'][$exercise]['selectedFile'])) { 
          $fileName = basename($toConvert['cycles'][$cycle]['days'][$day]['sections'][$section]['exercises'][$exercise]['selectedFile']['fileURL']); 
          if (isset($newFolderList['Exercises'][$fileName])) { 
           $toConvert['cycles'][$cycle]['days'][$day]['sections'][$section]['exercises'][$exercise]['selectedFile'] = $newFolderList['Exercises'][$fileName]['id']; 
          } 
         } 
        } 
       } 
      } 
     } 
    } 
    return $toConvert; 
} 

Model-> read() zasadniczo po prostu mówi Cakeowi, aby wyciągnął rekord z db, i zwraca go w tablicy. Jest mnóstwo rzeczy, które dzieją się za kulisami, ktoś bardziej kompetentny musiałby to wyjaśnić.

+0

Można spać na końcu każdej pętli, a jeśli używasz PHP 5.3 lub nowszej można (próbować) nazywamy śmieciarza. –

+0

W czym pomoże sen? –

+0

@therefromhere dać gc więcej czasu na kick lub zakończyć to, co robi. –

Odpowiedz

2

Pierwszym krokiem, który można zrobić, to upewnić się, że wszystko jest przekazywane przez odniesienie.

Np

foreach ($ids as $id) { 
processData($data); 
} 

function processData(&$d){} 

http://php.net/manual/en/language.references.pass.php

+0

Świetny pomysł! Biorę to z góry przyznane w JS ... –

+1

Czy byłoby rozsądnie wywołać 'unset()' siebie, jeśli dane nie są już potrzebne? –