@BarneyGumble

Как импортировать большой JSON (18гб) в MySQL?

Имеется файл JSON на 18гб. По сути он представляет из себя список объектов line-by-line и не является валидным JSON-ом.

Выглядит примерно так:
{"address": "Gabriele-Tergit-Promenade 19, Berlin", "amenity_groups": [{"amenities": ...}
{"address": "Passeig De Gracia, 68, Barcelona", "amenity_groups": [{"amenities": ...}
...
{"address": "Great Cumberland Place, London", "amenity_groups": [{"amenities": ...}


Мне нужно загнать всё это дело в MySQL. Какие наиболее оптимальные способы для этого есть?

На ум приходит написание PHP-скрипта, который считает json-файл и далее line-by-line получает json каждого отеля, перегоняет его в массив, парсит и делает insert в БД нужных данных. Проблема в том, что исходный файл 18гб и мой скрипт уже на старте чтения файла умрёт по использованию памяти, какой бы я её не выставлял (на VDS у меня 4 GB RAM).

Какие ещё есть идеи и варианты?
  • Вопрос задан
  • 409 просмотров
Решения вопроса 1
@Flying
Вам не нужно загружать файл в память, для этого существует потоковая обработка:

<?php
$fp = fopen('big-file.json', 'r');
if (!is_resource($fp)) {
    throw new \RuntimeException('Failed to open file for reading');
}
while (!feof($fp)) {
    $line = fgets($fp, 32768);  // Укажите лимит на одну запись
    if (empty($line)) {
        // Пропускаем пустые строки
        continue;
    }
    $data = json_decode($line, true, 512, JSON_THROW_ON_ERROR);
    // ... записываем в базу
}
fclose($fp);
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
Target1
@Target1
Одно из первых решений что пришло использовать SplFileObject .
Открыть как файл и пачками по 500/1000 записываит в бд

$file = './file.json';
$spl = new SplFileObject($file);
$spl->seek(177777);
echo $spl->current()
Ответ написан
Комментировать
romesses
@romesses
Backend инженер
Это файл формата JSONL.

JSON - родной формат для MySQL 5.7+ и каждую строку можно заносить в JSON колонку БД как есть, без предварительного декодирования в PHP. Можно и во временную таблицу, а затем извлечь конкретные поля.

То есть достаточно считывать строки и применяя yield, отправлять в буфер-коллектор. По достижению размера, скажем, 1000 строк, выполнять INSERT INTO ... VALUES (...), сбрасывая содержимое буфера.

https://www.w3schools.com/sql/sql_insert.asp
https://www.w3schools.com/sql/sql_insert_into_sele...

Или же импортировать файл утилитой Mysql Shell.
Ответ написан
Комментировать
@Akina
Сетевой и системный админ, SQL-программист.
Мне нужно загнать всё это дело в MySQL. Какие наиболее оптимальные способы для этого есть?

Оптимальный - положить этот файл туда, куда может дотянуться MySQL. Импортировать, используя LOAD DATA INFILE, во временную таблицу. Несложным запросом распарсить в рабочие таблицы (как я понимаю, хоть весь файл и невалиден как JSON, но каждая отдельная строка файла есть JSON валидный). И прибить временную таблицу.

На всё про всё три запроса. Если очень хочется, можно их выполнить и через php, конечно. Но я бы затолкал их в хранимую процедуру (особенно если задача импорта обновлённых данных будет регулярная) и вызывал её - тогда вообще один запрос CALL proc_name;.

А можно и в один запрос уложиться, если использовать LOAD DATA INFILE с препроцессингом. Тогда и валидность JSON на строку неважна =- лишь бы формат данных в строке не плавал от одной строки к другой.

А гонять 18 гектар с диска в PHP, а потом от PHP к MySQL - ну несерьёзно.

PS. Загрузка с использованием LOAD DATA INFILE весьма нетребовательна к объёму оперативной памяти. И неважно, какого размера исходный файл.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы