用Bash循环批量合并文件以进行mongoimport

3
我有一个目录,里面有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变成单行。但是,我不知道如何将两者合并为单个命令。

如果可能,请帮助解决这个问题的两个部分。


find 的一个目的是处理文件集合,这些文件太大而无法放在单个命令行中。你可能正在错误地使用 find - chepner
2个回答

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生成脚本的方法。 由于文件数量非常大,因此以下内容使用了在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

谢谢!第二个例子是用什么语言编写的? - tom
正如标题所说,jq :-) - peak

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

但是,有些文件导入失败了。我已经导入了105,000个文件,但在Mongo集合中只有98547个文件显示出来。我认为这是因为一些文档的大小超过了16MB。


将数百万个文件名存储在bash数组中可能不是一个好主意。这就是为什么awk和jq的“inputs”非常方便的原因。 - peak
你可以轻松修改基于awk的方法,以便按可变大小的组处理文件,使得组中文件大小之和不超过一个阈值。(例如,从ls -l | awk ...开始) - peak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接