【问题标题】:running parallel mongodump in bash script在 bash 脚本中运行并行 mongodump
【发布时间】:2017-10-22 04:41:52
【问题描述】:

编者注:这是关于running a specified number of commands in parallel 的更一般问题的后续问题。

我正在尝试为 20 个 mongodb 服务器运行这个 mongodb 备份脚本。

#!/bin/bash
#daily backup for mongo db's

BACKUP_HOSTS=(example.com staging.example.com prod.example.com example1.com)
#d=$(date +%Y-%m-%dT%H:%M:%S --date "-65 days")
d=$(date +%Y-%m-%dT%H:%M:%S --date "-5 days")
oid=$(mongo --quiet --eval "ObjectId.fromDate(ISODate('$d'))")

cd /data/daily/
rm -r /data/daily/*

TODAY=$(date +"%y-%m-%d")
mkdir "$TODAY"

cd $TODAY

#create subfolders

for HOST in ${BACKUP_HOSTS[@]}
do
        mkdir $HOST
done

#extract mongo dumps

echo "$(date '+%Y-%m-%d %H:%M:%S') start retrieving Mongodb backups"

for h in ${BACKUP_HOSTS[@]};do
    dbs=`mongo --eval "db.getMongo().getDBNames()" --host $h | grep '"' | tr -d '",' `
    for db in $dbs; do
       col=`mongo  $db --host $h --quiet --eval "db.getCollectionNames()" | tr -d ',"[]' `
       for collection in $col; do
            xargs -P 0 -n 1 mongodump --host $h -q "{_id:{\$gt:$oid}}" -d $db -c $collection --out /data/daily/$TODAY/$h
       done
    done
done

但不工作。

也尝试过:

parallel -P 0 -n 1 mongodump --host $h "{_id:{\$gt:$oid}}" -d $db -c $collection --out /data/daily/$TODAY/$h

但我明白了:

bin/bash: -c: line 0: syntax error near unexpected token `('

【问题讨论】:

  • 以后调试类似语法错误的一般提示:将set -x放在脚本顶部附近,您会立即看到额外的(来自哪里。
  • @Jens:一般来说这是个好建议,但在这种情况下无济于事,因为错误消息是由 GNU Parallel 发出的,它调用了bash在幕后 i>.
  • @mklement0 第一个脚本中没有使用 GNU 并行,并且不清楚错误消息不会在两个脚本中出现。我的怀疑是,$collection 有不带引号的括号。
  • @Jens:$collection 的猜测不错,但错误消息中的 -cline 0 告诉我们,它不是第一个产生错误的脚本。引发错误的简单方法:parallel echo 'a(' ::: 1。请注意 shell 命令本身在语法上是如何 有效的,但是 Parallel 默认调用它的方式会导致它中断。选项-q 解决了这个问题。

标签: bash mongodb parallel-processing xargs gnu-parallel


【解决方案1】:

试试

mongodump --host $h -q "{_id:{\$gt:$oid}}" -d $db -c $collection > /data/daily/$TODAY/$h &

最后的& 使命令在后台运行,因此循环的每个命令都将与前一个命令并行运行。也可以看看this

但是,我建议您始终将变量括在双引号中,例如 "$var",否则可能会出现许多异常并干扰您的命令的执行。 比如这个错误:

bin/bash: -c: line 0: 意外标记 `(' 附近的语法错误

这似乎是由您的变量 $collection 具有的一些特殊字符引起的。

因此,它的安全版本是:

mongodump --host "$h" -q "{_id:{\$gt:$oid}}" -d "$db" -c "$collection" > /data/daily/"$TODAY"/"$h" &

您可以查看here 为什么以及何时使用双引号了解更多详情。

【讨论】:

  • 问题中没有明确说明,但 OP 希望有一种 自动化 方式来运行 尽可能多的并行作业,而无需手动将分区管理为可以同时运行的批次并等待作业结束,以便可以启动新的作业(这是您必须使用& 做的事情)。重复引用的好建议。
【解决方案2】:

试试下面的xargs -P解决方案:

for h in "${BACKUP_HOSTS[@]}";do
  dbs=$(mongo --eval "db.getMongo().getDBNames()" --host "$h" | grep '"' | tr -d '",')
  for db in $dbs; do
    mongo "$db" --host "$h" --quiet --eval "db.getCollectionNames()" | tr -d ',"[]' |
      xargs -P 0 -n 1 mongodump --host "$h" -q "{_id:{\$gt:$oid}}" -d "$db" --out "/data/daily/$TODAY/$h" -c 
  done
done
  • xargs 仅对 stdin 输入进行操作,而您的解决方案尝试不提供任何标准输入;上述解决方案将集合名称检索mongo 的结果直接传递给xargs

    • 请注意,这假定集合名称既没有嵌入空格也没有嵌入 \ 字符。
  • -P 0 仅适用于 GNU xargs,它将0 解释为:“同时运行尽可能多的进程”(我不清楚它是如何定义的)。

  • for 循环命令输出通常是脆弱的

    • 只有在 (a) 每个空格分隔的单词都应被视为其自己的参数并且 (b) 这些单词不包含诸如 * 之类的通配符时,它才能可靠地工作 - 请参阅 this Bash FAQ entry
  • 请注意所有变量引用(除了那些已知仅包含 数字的)如何被双引号以保证稳健性。

  • 使用现代命令替换语法$(...),而不是旧语法`...`,即preferable


至于 GNU parallel 命令,请尝试以下变体,使用来自集合名称的标准输入输入 - 检索mongo 命令,如上

... | parallel -P 0 -N 1 -q mongodump --host "$h" -q "{_id:{\$gt:$oid}}" -d "$db" -c {1} --out "/data/daily/$TODAY/$h"
  • -N 1 而不是-n 1 允许您使用占位符{1}控制从标准输入读取参数在命令行中的放置位置

  • -q 确保传递带有双引号的复杂命令正确传递到 shell。

  • Shell 变量引用用双引号括起来,以确保按原样使用。


故障排除xargs / GNU parallel 调用:

xargs 和 GNU parallel 都支持 -t(GNU 并行:别名 --verbose):

  • -t 将每个命令行打印到 stderr就在它启动之前
  • 警告:使用xargs,输入引用将不会反映在打印的命令中,因此您将无法验证指定的参数边界。

xargs -t 示例:

$ time echo '"echo 1; sleep 1" "echo 2; sleep 2" "echo 3; sleep 1.5"' |
    xargs -t -P 2 -n 1 sh -c 'eval "$1"' -

这会产生类似:

sh -c eval "$1" - echo 1; sleep 1
sh -c eval "$1" - echo 2; sleep 2
2
1
sh -c eval "$1" - echo 3; sleep 1.5
3

real    0m2.535s
user    0m0.013s
sys 0m0.013s

注意:

  • 命令行缺少对参数的原始引用,但仍按最初指定的方式执行调用。

  • 它们在命令启动之前立即打印(到 stderr)。

  • 如前所述,来自命令的输出可能会乱序到达,并且无法预料地交错。

  • 整体执行耗时约 2.5 秒,分解如下:

    • 由于 -P 2echo 1; ...echo 2; ... 命令并行运行,而 echo 3; ... 命令作为 3rd 命令最初被阻止,因为没有一次允许运行 2 个以上的命令。

    • 1 秒后,echo 1; ... 命令完成,将运行的并行进程计数降至 1,触发执行剩余的 echo 3; ... 命令。

    • 因此,因为最后一个命令在 1 秒后启动并运行了 1.5 秒,所以最后一个命令在 ca 之后完成。 2.5 秒(而前 2 个命令分别在 1 秒和 2 秒后完成)。

GNU parallel -t 示例:

$ time echo $'echo 1; sleep 1\necho 2; sleep 2\necho 3; sleep 1.5' | 
    parallel -P 2 -q -t sh -c 'eval "$1"' -

sh -c eval\ \"\$1\" - echo\ 1\;\ sleep\ 1
sh -c eval\ \"\$1\" - echo\ 2\;\ sleep\ 2
1
sh -c eval\ \"\$1\" - echo\ 3\;\ sleep\ 1.5
2
3

real    0m2.768s
user    0m0.165s
sys 0m0.094s

注意:

  • 因为该命令使用引号来划分参数,并且必须将划分传递给sh,所以还必须指定-q

  • \-quoting 可能看起来不寻常,但它是正确的 shell 引用,并且完全反映了在幕后调用的命令。

  • GNU parallel 期望参数默认为 行分隔,因此 shell 命令使用带有 @ 的 ANSI C 引用字符串 ($'...') 在单独的行上传递987654369@ 转义序列。

  • 总体处理时间比 xargs 长,这是您为 GNU parallel 的附加功能和技术基础(Perl 脚本)支付的 - 可能可以忽略不计的价格。

  • 这些附加功能之一是上述输出序列化(分组):1st 命令的输出可预测首先出现,即使它和第二个命令启动同时;第二个命令的输出直到 完成后才打印出来(这就是为什么首先显示第三个命令行的诊断打印的原因)。

GNU parallel 还支持--dry-run,它打印命令 - 到stdout - 而不实际运行它们。

$ echo $'echo 1; sleep 1\necho 2; sleep 2\necho 3; sleep 1.5' |
    parallel -P 2 -q --dry-run sh -c 'eval "$1"' -

sh -c eval\ \"\$1\" - echo\ 1\;\ sleep\ 1
sh -c eval\ \"\$1\" - echo\ 2\;\ sleep\ 2
sh -c eval\ \"\$1\" - echo\ 3\;\ sleep\ 1.5

【讨论】:

  • @basante:这不完全是MCVE (Minimal, Complete, and Verifiable Example) - 有不相关的代码,必须安装 MongoDB,但你的问题在于如何调用 xargsparallel - 不太可能不相关到mongo。我在答案中添加了故障排除部分。看看这是否可以帮助您解决问题;如果没有,请将您的问题缩小到 MCVE。
【解决方案3】:

只需要在末尾加一个&

for collection in $cols; do
        mongodump --host "$h" -q "{_id:{\$gt:$oid}}" -d "$db" -c 
"$collection" --out /data/daily/$TODAY/$h
    done &

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-24
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-16
    • 1970-01-01
    • 2016-01-31
    相关资源
    最近更新 更多