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, мать его".