Вариант использования
Мне нужно разбить большие файлы (~5G
) данных JSON на файлы меньшего размера с разделителями новой строки JSON с эффективным использованием памяти (т.е. , без необходимости читать весь JSON blob в память). Данные JSON в каждом исходном файле представляют собой массив объектов.
К сожалению, исходные данные не разделены новой строкой JSON и в некоторых случаях в файлах вообще нет новых строк. Это означает, что я не могу просто использовать команду split
, чтобы разделить большой файл на более мелкие части с помощью новой строки. Вот примеры того, как исходные данные хранятся в каждом файле:
Пример исходного файла с символами новой строки.
[{"id": 1, "name": "foo"}
,{"id": 2, "name": "bar"}
,{"id": 3, "name": "baz"}
...
,{"id": 9, "name": "qux"}]
Пример исходного файла без символов новой строки.
[{"id": 1, "name": "foo"}, {"id": 2, "name": "bar"}, ...{"id": 9, "name": "qux"}]
Вот пример желаемого формата для одного выходного файла:
{"id": 1, "name": "foo"}
{"id": 2, "name": "bar"}
{"id": 3, "name": "baz"}
Текущее решение
Я могу достичь желаемого результата, используя jq
и split
, как описано в этом сообщении SO . Такой подход эффективен с точки зрения памяти благодаря потоковому парсеру jq
. Вот команда, которая достигает желаемого результата:
cat large_source_file.json \
| jq -cn --stream 'fromstream(1|truncate_stream(inputs))' \
| split --line-bytes=1m --numeric-suffixes - split_output_file
Проблема
Приведенная выше команда требует ~47 mins
для обработки всего исходного файла. Это кажется довольно медленным, особенно по сравнению с sed
, который может производить тот же результат намного быстрее.
Вот несколько тестов производительности, чтобы показать время обработки с jq
по сравнению с sed
.
export SOURCE_FILE=medium_source_file.json # smaller 250MB
# using jq
time cat ${SOURCE_FILE} \
| jq -cn --stream 'fromstream(1|truncate_stream(inputs))' \
| split --line-bytes=1m - split_output_file
real 2m0.656s
user 1m58.265s
sys 0m6.126s
# using sed
time cat ${SOURCE_FILE} \
| sed -E 's#^\[##g' \
| sed -E 's#^,\{#\{#g' \
| sed -E 's#\]$##g' \
| sed 's#},{#}\n{#g' \
| split --line-bytes=1m - sed_split_output_file
real 0m25.545s
user 0m5.372s
sys 0m9.072s
Вопросы
- Ожидается ли такая более низкая скорость обработки для
jq
по сравнению с sed
? Имеет смысл, что jq
будет медленнее, учитывая, что он выполняет много проверок под капотом, но в 4 раза медленнее не кажется правильным. - Могу ли я что-нибудь сделать, чтобы улучшить скорость, с которой
jq
может обработать этот файл? Я бы предпочел использовать jq
для обработки файлов, потому что я уверен, что он может беспрепятственно обрабатывать другие форматы вывода строк, но, учитывая, что я обрабатываю тысячи файлов каждый день, трудно оправдать наблюдаемую мной разницу в скорости.