XML любят те, кто его не парсил. На бумаге красиво: читаемый формат, структура, всё по полочкам. На практике — жрёт память, тормозит, и любой лишний пробел превращается в геморрой.
Я не загружал 50-гиговые монстры, но гигабайт-два уже хватает, чтобы PHP начал хрипеть. И если ты полезешь туда с simplexml_load_file — поздравляю, словишь Allowed memory size exhausted.
Первое правило: не грузить всё разом
Учебниковый пример "открыл файл целиком и пошёл по узлам" работает только на XML в пару мегов. Дальше — смерть.
Реальная жизнь — это XMLReader. Он тянет файл потоками. Ты читаешь кусок, обработал, вставил в базу, пошёл дальше. Никаких попыток держать всё в памяти.
Второе правило: MySQL не обязан страдать
LOAD XML INFILE звучит красиво, но если файл больше пары сотен мегов — начинаются пляски: таймауты, ошибки клиента, подвисающий сервер.
Выход простой: нарезаешь данные на батчи. Например, собрал 1000 записей — одним INSERT ... VALUES. Дальше ещё тысячу. Построчная вставка — это издевательство и над собой, и над базой.
Третье правило: оптимизация перед стартом
- Индексы лучше вырубить на время импорта. Потом включить обратно — будет быстрее.
- Транзакции спасают, если что-то упало на середине.
- Память и буферы MySQL лучше подстроить заранее.
Минимальный рабочий пример
<?php $pdo = new PDO("mysql:host=localhost;dbname=test", "root", "pass", [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
$reader = new XMLReader();
$reader->open('data.xml');
$batch = [];
$count = 0;
while ($reader->read()) {
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name === 'item') {
$node = $reader->expand();
$simple = simplexml_import_dom($node);
$batch[] = [
'col1' => (string)$simple->column1,
'col2' => (string)$simple->column2
];
if (++$count % 1000 === 0) {
insertBatch($pdo, $batch);
$batch = [];
}
}
}
if ($batch) insertBatch($pdo, $batch);
function insertBatch($pdo, $batch) {
$sql = "INSERT INTO your_table (column1, column2) VALUES ";
$params = [];
foreach ($batch as $row) {
$params[] = "(". $pdo->quote($row['col1']).", ".$pdo->quote($row['col2']).")";
}
$pdo->exec($sql . implode(",", $params));
}
Да, выглядит топорно, но зато работает, когда файл уже больше гига.
Типичные косяки
- Парсинг целиком — смерть PHP.
- Построчная вставка — импорт будешь ждать дольше, чем зарплату.
- "Да чё там, база выдержит" — ага, как же.
Итого
Импорт XML — это не про красоту. Это про "как бы не упасть лицом в клаву". Потоковый парсинг и батчи — единственное, что держит всё в рамках.
Если прилетает большой XML, то это не повод геройствовать, а повод сразу думать о потоках и оптимизации. В следующий раз, когда кто-то предложит хранить данные в XML, смело отвечай: "давайте JSON, мать его".
0 комментариев