отделите сохраненный вывод команды, обрабатывающей очень большой сжатый файл JSON - PullRequest
2 голосов
/ 16 марта 2019

Хорошо, давайте начнем с командной строки, которую я использую:

curl --silent http://example.com/json.gz | pigz -dc | jq -r '[.name, .value] | @csv' > data.csv

CURL загрузит сжатый файл JSON размером 11,6 ГБ, pigz распакует его и запишет весь обработанный вывод вstdout, jq прочитает JSON и сохранит вывод как файл csv.

Проблема в том, что вывод, сохраненный как data.csv, чрезвычайно велик, и в конце концов мне все еще нужно проанализировать эти данные с помощьюPHP-скрипт и вставьте его в MYSQL в специальном формате (тогда размер данных будет очень маленьким)

Но на моем сервере осталось менее 60 ГБ свободного места, даже я не могу распаковатьполные данные и сохранить их в файл CSV.

Итак, у меня появилась идея, если я смогу сохранить выходные данные в отдельные файлы, которые имеют разные имена (скажем, имена являются текущей датой илиотметка времени), затем я могу запустить скрипт PHP, чтобы обработать каждый из них .csv-файл и сохранить данные в БД, а затем удалить файл, чтобы освободить место, не уверен, что это лучший способ, но, по крайней мере, я пытаюсь сделатьэто работает.

Итак, я изменил свою командную строку на:

curl --silent http://example.com/json.gz | pigz -dc | jq -r '[.name, .value] | @csv' > `date +"%S-%M-%d-%m-%Y"`_data.csv

Но он сохранил все это только в одном файле, я думал, что он сохранит его как несколько файлов, которые каждый изу них другое имя, так как дата будет продолжать меняться, пока записывается вывод.

Также приветствуются любые другие рабочие решения, спасибо!

Ответы [ 3 ]

3 голосов
/ 20 марта 2019

Используйте команду split, см. man-page

Простой пример (от 10 МБ до STDOUT):

# dd if=/dev/zero bs=1M count=10 | split - --bytes=1M -d -a3 out

Выходные файлы (10 файлов размером 1 МБ, считанные из STDIN):

# stat -c "%s %n" out00*
1048576 out000
1048576 out001
1048576 out002
1048576 out003
1048576 out004
1048576 out005
1048576 out006
1048576 out007
1048576 out008
1048576 out009

Или разделить сохраненный файл с помощью split --bytes=1M -d -a3 out out

Выход:

# stat -c "%s %n" out*
10485760 out
1048576 out000
1048576 out001
1048576 out002
1048576 out003
1048576 out004
1048576 out005
1048576 out006
1048576 out007
1048576 out008
1048576 out009
3 голосов
/ 24 марта 2019

экономия места с помощью GNU split --filter

POSIX split создает выходные файлы из своего ввода и поэтому требует много свободного места для их хранения (размер всего несжатого ввода плюс некоторые накладные расходы).

Однако версия GNU split имеет дополнительную опцию --filter, которая позволяет обрабатывать отдельные фрагменты данных в гораздо меньшем пространстве, поскольку для этого не требуется создавать какие-либо временные файлы:

| split -l $NUMLINES --filter='shell_command'

Вы можете думать об этом как xargs -n $NUMLINES command, за исключением передачи данных в stdin вместо аргументов командной строки.

Например, чтобы вывести сумму md5 каждого набора (до) 7 строк в / etc / passwd, а затем вывести количество обработанных кусков:

</etc/passwd split -l7 --filter='md5sum|tee /dev/tty' |\
{ echo Processed $(wc -l) chunks; }

Чтобы ваша команда работала на 10000 строк одновременно, вы можете сделать что-то вроде:

curl -L --silent "$URL" |\
pigz -dc |\
jq -r '[.name, .value] | @csv' |\
split -l 10000 --filter='save2db.php'

Ваша команда фильтра save2db.php должна считываться со стандартного ввода.

Если вы предпочитаете, чтобы он читался из реального файла, вы можете сделать что-то вроде:

... |\
split -l 10000 --filter='cat >TMPFILE; save2db.php TMPFILE';
rm TMPFILE

Предупреждение: Вам необходимо убедиться, что безопасно разделить ваш CSV-файл по границам строк. Некоторые CSV-файлы содержат поля со встроенными литеральными символами новой строки; они могут стать деформированными, если разделить середину поля.

1 голос
/ 16 марта 2019

Я бы предложил использовать такую ​​программу, как awk, чтобы выполнить разбиение, например вот так:

jq -rc '[.id, .value] | @csv' |
  awk -v NUM 100000 '{n++; print > "out." int((n+NUM)/NUM) ".csv"}'
...