我有一个包含250万个小JSON文件的目录。它在磁盘上是104gb。他们是多行文件。
我想从文件中创建一组JSON数组,以便我可以在合理的时间内使用mongoimport导入它们。这些文件不能超过16mb,但即使我设法将它们设置为10个,我也会感到高兴。
到目前为止,我可以使用它以每分钟1000左右的速度进行一次:
for i in *.json; do mongoimport --writeConcern 0 --db mydb --collection all --quiet --file $i; done
我想我可以使用" jq"要做到这一点,但我不知道如何使bash循环一次传递10个文件到jq。 请注意,使用bash查找结果会导致错误,因为文件太多。
使用jq,你可以使用--slurp来创建数组,使用-c来创建多行json单行。但是,我无法看到如何将两者合并为一个命令。
如果可能,请帮助解决问题的两个部分。
答案 0 :(得分:2)
这是一种方法。为了说明,我使用了awk,因为它可以小批量读取文件列表,因为它能够执行jq和mongoimport。您可能需要进行一些调整以使整个事情更加健壮,以测试错误,等等。
想法是生成可以查看然后执行的脚本,或者使用awk的system()命令直接执行命令。首先,让我们生成脚本:
ls *.json | awk -v group=10 -v tmpfile=json.tmp '
function out() {
print "jq -s . " files " > " tmpfile;
print "mongoimport --writeConcern 0 --db mydb --collection all --quiet --file " tmpfile;
print "rm " tmpfile;
files="";
}
BEGIN {n=1; files="";
print "test -r " tmpfile " && rm " tmpfile;
}
n % group == 0 {
out();
}
{ files = files " \""$0 "\"";
n++;
}
END { if (files) {out();}}
'
验证完成后,您可以执行生成的脚本,也可以更改“print ...”行以使用“system(....)”
这是一种仅用于生成脚本的jq方法。 由于文件数量非常大,以下使用仅在jq 1.5中引入的功能,因此其内存使用情况与上面的awk脚本类似:
def read(n):
# state: [answer, hold]
foreach (inputs, null) as $i
([null, null];
if $i == null then .[0] = .[1]
elif .[1]|length == n then [.[1],[$i]]
else [null, .[1] + [$i]]
end;
.[0] | select(.) );
"test -r json.tmp && rm json.tmp",
(read($group|tonumber)
| map("\"\(.)\"")
| join(" ")
| ("jq -s . \(.) > json.tmp", mongo("json.tmp"), "rm json.tmp") )
调用:
ls *.json | jq -nRr --arg group 10 -f generate.jq
答案 1 :(得分:0)
这是我想出的。它似乎工作,并以大约80秒的速度导入外部硬盘驱动器。
#!/bin/bash
files=(*.json)
for((I=0;I<${#files[*]};I+=500)); do jq -c '.' ${files[@]:I:500} | mongoimport --writeConcern 0 --numInsertionWorkers 16 --db mydb --collection all --quiet;echo $I; done
然而,有些人失败了。我已经导入了105k文件,但mongo集合中只出现了98547个文件。我认为这是因为有些文件是&gt; 16MB。