У меня есть таблица MySQL InnoDB с 1 миллионом строк, и я выбираю 100K строк для экспорта. Таблица содержит около 200 столбцов.
Что я сделал до сих пор:
не выберите все с *
SELECT column1, column2, … FROM my_table WHERE deleted = 0 – загрузка 100k записей
с использованием библиотеки XMLWriter php с флешем
$writer = new XMLWriter(); $writer->openMemory(); $writer->setIndent(true); $writer->startDocument('1.0', 'UTF-8'); $writer->startElement('export'); $iterator = 0; $data = $this->getData(); foreach($adverts as $advert) { $writer->startElement('ad'); $writer->writeElement('id', $data->id); // .. other columns $writer->endElement(); // end ad if (0 == $iterator % 1000) { file_put_contents($this->getFilePath(), $writer->flush(TRUE), FILE_APPEND); } $iterator++; }
Но у меня все еще есть фатальная ошибка: допустимый размер памяти … байт исчерпан
Есть ли другие способы, как оптимизировать это? Я думаю, что могу загружать данные из базы данных другими способами, например, загружать только идентификаторы в первом раунде, а затем выбирать IN (10k_ids), но пока не проверяю эту идею.
Спасибо за ваше мнение.
У меня очень похожий вопрос, как в том, как экспортировать таблицу HTML со 100 тыс. Записей с форматированием номера без выхлопа памяти
Но нет способа добиться низкой памяти.
Я нашел решение, проблема заключалась в том, что я загружаю много данных.
Я сделал 3 обновления:
использовать функцию для ограничения объема памяти журнала
/** * @param $message */ protected function logMemoryUsage($message) { Debugger::log($message . ": " . memory_get_usage()/1048576 ." MB"); }
то я использую fopen + fwrite + fclose вместо file_put_contents
$file = fopen($this->getFilePath(), 'a+'); fwrite($file, $writer->flush(TRUE)); fclose($file);
загружать данные в цикле (всего за 10 000 записей за один раз)
$this->logMemoryUsage("Memory usage before load"); $data = $this->getData($lastId); do { $this->logMemoryUsage("Memory usage"); $lastId = NULL; foreach($data as $item) { $writer->startElement('ad'); $writer->writeElement('id', $item->id); ... if (0 == $iterator % 5000) { fwrite($file, $writer->flush(TRUE)); } $lastId = $item->id; $iterator++; } $data = $this->getData($lastId); } while(!empty($data)); $this->logMemoryUsage("Memory usage after"); fwrite($file, $writer->flush(TRUE)); fclose($file); public function getData($fromId = NULL, $limit = 10000) { $data = db::query("SELECT a,b,c FROM my_table WHERE deleted=0 AND id>? ORDER BY id ASC LIMIT ?", $fromId, $limit)->fetchAll(); }
-$this->logMemoryUsage("Memory usage before load"); $data = $this->getData($lastId); do { $this->logMemoryUsage("Memory usage"); $lastId = NULL; foreach($data as $item) { $writer->startElement('ad'); $writer->writeElement('id', $item->id); ... if (0 == $iterator % 5000) { fwrite($file, $writer->flush(TRUE)); } $lastId = $item->id; $iterator++; } $data = $this->getData($lastId); } while(!empty($data)); $this->logMemoryUsage("Memory usage after"); fwrite($file, $writer->flush(TRUE)); fclose($file); public function getData($fromId = NULL, $limit = 10000) { $data = db::query("SELECT a,b,c FROM my_table WHERE deleted=0 AND id>? ORDER BY id ASC LIMIT ?", $fromId, $limit)->fetchAll(); }
И выход теперь:
export start Memory usage before load: 3.6202011108398 MB Memory usage: 59.487106323242 MB Memory usage: 124.53610229492 MB Memory usage: 124.89745330811 MB Memory usage: 124.43883514404 MB Memory usage: 124.20503234863 MB Memory usage: 124.2151184082 MB Memory usage: 124.46990966797 MB Memory usage: 106.50185394287 MB Memory usage: 53.009048461914 MB export end