Циклический просмотр большого файла заканчивается нехваткой памяти

[ОТРЕДАКТИРОВАНО, ЗДЕСЬ КОРОТКАЯ ВЕРСИЯ]

Циклический просмотр файла и чтение содержимого, а затем запись приводят к сбою функции. Оказалось проблема с памятью. Это три версии, которые я пробовал.

Сначала попробовал это:

$file = new SplFileObject($this->getDirectoryPath() . $this->getFileName(), "a+");
$file->setFlags(SplFileObject::DROP_NEW_LINE | SplFileObject::SKIP_EMPTY);

if ($this->exists()) {
    foreach ($file as $line) {
        $tempArray = unserialize($line);
        if ($tempArray['Key'] == $arrayOfData['Key']) {
            foreach ($totalsToBeAdded as $key) {
                $arrayOfData[$key] += $tempArray[$key];
            }
        }
    }
}

$tempString = serialize($arrayOfData);

$file->fwrite("$tempString\r\n");

$this->numLines++;

Затем я попробовал это:

$file = new SplFileObject($this->getDirectoryPath() . $this->getFileName(), "a+");
$file->setFlags(SplFileObject::DROP_NEW_LINE | SplFileObject::SKIP_EMPTY);

if ($this->exists()) {
    while (!$file->eof()) {
        $tempArray = unserialize($file->current());
        if ($tempArray['PartNumber'] == $arrayOfData['PartNumber']) {
            foreach ($totalsToBeAdded as $key) {
                $arrayOfData[$key] += $tempArray[$key];
            }
        }

        $file->next();
    }
}

$tempString = serialize($arrayOfData);

$file->fwrite("$tempString\r\n");

$this->numLines++;

И, наконец, я отказался от SplFileObject и просто пошел с обычным fopen и т. д.:

$handle = fopen($this->getDirectoryPath() . $this->getFileName(), "a+");

if ($this->exists()) {
    while (false !== ($line = fgets($handle))) {
        $tempArray = unserialize(trim($line));
        if ($tempArray['Key'] == $arrayOfData['Key']) {
            foreach ($totalsToBeAdded as $key) {
                $arrayOfData[$key] += $tempArray[$key];
            }
        }
    }
}

$tempString = serialize($arrayOfData);
fwrite($handle, "$tempString\r\n");
fclose($handle);
$this->numLines++;

РЕДАКТИРОВАТЬ ДЛЯ ДОПОЛНИТЕЛЬНОЙ ИНФОРМАЦИИ:

Мне было любопытно, использует ли базовый код PHP массив для итераторов при проходе строки за строкой через файл, что может убить его.

Кроме того, файл начинает создаваться, я могу наблюдать, как он пишет, пока не достигнет 500-600 КБ, а затем умирает.

Окончательный размер файла будет около 10 мб.

Последнее обновление:

Это работает (обратите внимание на отсутствие открытия и чтения файла):

public function writeUnique($arrayOfData, $totalsToBeAdded) {  
        $tempArray = array();

        $handle = fopen($this->fullPath, "a+");

        $tempString = serialize($arrayOfData);
        fwrite($handle, "$tempString\r\n");
        fclose($handle);
        $this->numLines++;
}

Пока это ломается (обратите внимание, что ВСЕ, что делается, зацикливается на всем файле, ТОГДА записывается в файл):

public function writeUnique($arrayOfData, $totalsToBeAdded) {  
        $tempArray = array();

        $handle = fopen($this->fullPath, "a+");

        if ($this->exists()) {
            while (false !== ($line = fgets($handle))) {

            }
        }

        $tempString = serialize($arrayOfData);
        fwrite($handle, "$tempString\r\n");
        fclose($handle);
        $this->numLines++;
}

ОБНОВЛЕНИЕ НОМЕР ТРЕТЬЕ:

Я сейчас проверил это:

public function writeUnique($arrayOfData, $totalsToBeAdded) {

    $handle = fopen($this->fullPath, "a+");

    if ($this->exists()) {
        while (false !== ($line = fgets($handle))) {

        }
    }

    $tempString = serialize($arrayOfData);
//        fwrite($handle, "$tempString\r\n"); Commented out the writing.
    fclose($handle);
    $this->numLines++;
}

Это сработало. Никаких сбоев, ошибок памяти или других ошибок.

Таким образом, похоже, что это либо проблема с итерациями повторного чтения одних и тех же строк большого файла, либо часть функции записи каким-то образом наступает на пятки функции чтения... что, честно говоря, не имеет смысла . Я знаю, что все думали, что это как-то связано с моими массивами. Но я очень сильно убрал ВСЮ свою логику, и я просто пытаюсь читать/писать большой файл.


person defaultNINJA    schedule 02.11.2012    source источник
comment
trime($line) это опечатка, и вы имели в виду trim, или это пользовательская функция, которую вы сделали? Очевидно, что в PHP нет функции trime()   -  person Anthony Hatzopoulos    schedule 02.11.2012
comment
stackoverflow.com/questions/2461762/force-freeing-memory -в php   -  person Prof. Falken    schedule 02.11.2012
comment
Ни один из ваших примеров не показывает, откуда берутся переменные $arrayOfData или $totalsToBeAdded. Я подозреваю, что когда вы анализируете файл, вы постоянно добавляете эти переменные и в конечном итоге не хватает места?   -  person D-Rock    schedule 02.11.2012
comment
@D-Rock: нет, $arrayOfData — это массив, полученный из запроса к базе данных. Это ключ => значение настройки 10 столбцов.   -  person defaultNINJA    schedule 02.11.2012
comment
@Anthony: Вы правы, это просто опечатка. Это правильно в исходном коде.   -  person defaultNINJA    schedule 02.11.2012
comment
Спасибо за эту ссылку, Кларк. Я вижу, что concat может быть проблемой для меня. Я также попытаюсь передать больше переменных, а не создать их в цикле.   -  person defaultNINJA    schedule 02.11.2012


Ответы (2)


Пытаться:

if ($this->exists()) {
    while (false !== ($line = fgets($handle))) {
        $tempArray = unserialize(trim($line));
        unset($line);
        if ($tempArray['Key'] == $arrayOfData['Key']) {
            foreach ($totalsToBeAdded as $key) {
                $arrayOfData[$key] += $tempArray[$key];
            }
        }
        unset($tempArray);
    }
}

Единственные персистентные массивы, которые я вижу здесь, это $totalsToBeAdded и $arrayOfData, которые выглядят одномерными из вашего оператора +=, так что вы мало что можете сделать, кроме микрооптимизации.

person Asad Saeeduddin    schedule 02.11.2012
comment
Я тоже так думал, за исключением того, что $totalsToBeAdded просто жестко закодировано '$totalsToBeAdded = array('stuff', 'stuff' и т. д.);' Там всего 9 штук. - person defaultNINJA; 02.11.2012
comment
Пробовал отменить команду. Не помогло. Хотя спасибо за идею. - person defaultNINJA; 02.11.2012

Итак, я, наконец, просто сломался и сделал математику, чтобы выяснить, сколько циклов мне нужно, чтобы php выполнил в этом файле, и число составляет 8 788 338 000 000 раз.

Это, в свою очередь, привело к тайм-ауту PHP. Чтобы предотвратить истечение времени ожидания, эту строку кода необходимо было добавить.

set_time_limit(0); // ignore php timeout

Теперь все временные файлы можно читать и анализировать построчно. Однако для больших файлов (10 МБ +) время для завершения функции составляет более часа (она все еще работает, поскольку я вижу, что временный файл становится больше).

Я пришел к выводу, что если важна скорость, то, вероятно, будет лучше хранить БОЛЬШИЕ наборы данных во временной таблице SQL. Раньше это не было для меня вариантом, но теперь я форсирую проблему с власть предержащими, чтобы разрешить это. В худшем случае, senerio, это, по крайней мере, позволит ему работать.

ВНИМАНИЕ: ЭТО ПОЗВОЛЯЕТ БЕСКОНЕЧНОМУ ЦИКЛУ БЫТЬ ВЕЧНО И ВОЗМОЖНО УБИТЬ СЕРВЕР. УБЕДИТЕСЬ, ЧТО ВЫ ЗНАЕТЕ, КАК УБИТЬ ПРОЦЕСС ЧЕРЕЗ UNIX, ПЕРЕД ПОПЫТКОЙ.

person defaultNINJA    schedule 05.11.2012