编者注:这是关于running a specified number of commands in parallel的更一般的问题的后续问题。
我正在尝试为20个mongodb服务器运行这个mongodb备份脚本。
LONGLONG SectorsPerPartition = PartitionEntry->PartitionLength.QuadPart / dg.BytesPerSector;
但是没有用。
也试过:
int __cdecl SortPartitions(PPARTITION_INFORMATION_EX PartitionEntry1, PPARTITION_INFORMATION_EX PartitionEntry2)
{
if (!PartitionEntry1->PartitionNumber) return PartitionEntry2->PartitionNumber ? -1 : 0;
if (!PartitionEntry2->PartitionNumber) return +1;
if (PartitionEntry1->StartingOffset.QuadPart < PartitionEntry2->StartingOffset.QuadPart) return -1;
if (PartitionEntry1->StartingOffset.QuadPart > PartitionEntry2->StartingOffset.QuadPart) return +1;
return 0;
}
DWORD ExtendTest(HANDLE hDisk)
{
STORAGE_DEVICE_NUMBER sdn;
ULONG dwBytesRet;
if (!DeviceIoControl(hDisk, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesRet, NULL))
{
return GetLastError();
}
if (sdn.DeviceType != FILE_DEVICE_DISK || sdn.PartitionNumber != 0)
{
return ERROR_GEN_FAILURE;
}
GET_LENGTH_INFORMATION gli;
if (!DeviceIoControl(hDisk, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), &dwBytesRet, NULL))
{
return GetLastError();
}
DbgPrint("Disk Length %I64x (%I64u)\n", gli.Length.QuadPart, gli.Length.QuadPart);
PVOID stack = alloca(guz);
union {
PVOID buf;
PDRIVE_LAYOUT_INFORMATION_EX pdli;
};
ULONG cb = 0, rcb, PartitionCount = 4;
for (;;)
{
if (cb < (rcb = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[PartitionCount])))
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (DeviceIoControl(hDisk, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, buf, cb, &dwBytesRet, NULL))
{
if (PartitionCount = pdli->PartitionCount)
{
PPARTITION_INFORMATION_EX PartitionEntry = pdli->PartitionEntry;
qsort(PartitionEntry, PartitionCount, sizeof(PARTITION_INFORMATION_EX),
(int (__cdecl *)(const void *, const void *))SortPartitions );
do
{
if (!PartitionEntry->PartitionNumber)
{
continue;
}
LARGE_INTEGER EndOffset;
LARGE_INTEGER MaximumOffset = PartitionCount != 1 ? (PartitionEntry + 1)->StartingOffset : gli.Length;
EndOffset.QuadPart = PartitionEntry->StartingOffset.QuadPart + PartitionEntry->PartitionLength.QuadPart;
if (EndOffset.QuadPart > MaximumOffset.QuadPart)
{
//??
__debugbreak();
}
else if (EndOffset.QuadPart < MaximumOffset.QuadPart)
{
DISK_GROW_PARTITION dgp;
dgp.PartitionNumber = PartitionEntry->PartitionNumber;
dgp.BytesToGrow.QuadPart = MaximumOffset.QuadPart - EndOffset.QuadPart;
WCHAR sz[128];
swprintf(sz, L"\\\\?\\GLOBALROOT\\Device\\Harddisk%d\\Partition%u", sdn.DeviceNumber, dgp.PartitionNumber);
HANDLE hPartition = CreateFile(sz, FILE_READ_ACCESS|FILE_WRITE_ACCESS, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hPartition != INVALID_HANDLE_VALUE)
{
// +++ begin extend
BOOL fOk = FALSE;
DISK_GEOMETRY dg;
if (DeviceIoControl(hPartition, IOCTL_DISK_GROW_PARTITION, &dgp, sizeof(dgp), 0, 0, &dwBytesRet, 0) &&
DeviceIoControl(hPartition, IOCTL_DISK_UPDATE_DRIVE_SIZE, 0, 0, &dg, sizeof(dg), &dwBytesRet, 0) &&
DeviceIoControl(hPartition, IOCTL_DISK_GET_PARTITION_INFO_EX, 0, 0, PartitionEntry, sizeof(*PartitionEntry), &dwBytesRet, 0)
)
{
LONGLONG SectorsPerPartition = PartitionEntry->PartitionLength.QuadPart / dg.BytesPerSector;
fOk = DeviceIoControl(hPartition, FSCTL_EXTEND_VOLUME, &SectorsPerPartition,
sizeof(SectorsPerPartition), 0, 0, &dwBytesRet, 0);
}
if (!fOk)
{
GetLastError();
}
//--- end extend
CloseHandle(hPartition);
}
}
// else EndOffset.QuadPart == MaximumOffset.QuadPart - partition can not be extended
} while (PartitionEntry++, --PartitionCount);
}
return NOERROR;
}
switch (ULONG err = GetLastError())
{
case ERROR_MORE_DATA:
PartitionCount = pdli->PartitionCount;
continue;
case ERROR_BAD_LENGTH:
case ERROR_INSUFFICIENT_BUFFER:
PartitionCount <<= 1;
continue;
default:
return err;
}
}
}
DWORD ExtendTest()
{
HANDLE hDisk = CreateFileW(L"\\\\?\\PhysicalDrive0", FILE_GENERIC_READ|FILE_GENERIC_WRITE,
FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hDisk != INVALID_HANDLE_VALUE)
{
DWORD err = ExtendTest(hDisk);
CloseHandle(hDisk);
return err;
}
return GetLastError();
}
但我明白了:
#!/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
答案 0 :(得分:1)
尝试
mongodump --host $h -q "{_id:{\$gt:$oid}}" -d $db -c $collection > /data/daily/$TODAY/$h &
最后&
使命令在后台运行,因此循环的每个命令都将与前一个命令并行运行。请看this。
但是,我建议您始终将变量用双引号括起来,如"$var"
,否则可能会发生许多异常并干扰命令的执行。
比如这个错误:
bin / bash:-c:第0行:意外令牌附近的语法错误`(&#39;
似乎是由您的变量 $ collection 的某些特殊字符引起的。
因此,它的安全版本将是:
mongodump --host "$h" -q "{_id:{\$gt:$oid}}" -d "$db" -c "$collection" > /data/daily/"$TODAY"/"$h" &
您可以查看here为什么以及何时使用双引号获取更多详细信息。
答案 1 :(得分:0)
尝试以下 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 输入进行操作,而您的解决方案尝试不提供任何stdin输入;上面的解决方案将集合名称的结果 - mongo
直接检索到xargs
。
\
字符。 -P 0
仅适用于 GNU xargs
,它将0
解释为:“同时运行尽可能多的进程”(我是不清楚如何定义)。
使用for
循环命令输出通常脆弱。
*
之类的通配字符时,它才能可靠地工作 - 见this Bash FAQ entry 请注意所有变量引用(已知仅包含数字的引用除外)是双引号的健壮性。
使用现代命令替换语法$(...)
代替遗留语法`...`
,即preferable。
对于 GNU parallel
命令,请尝试以下变体,使用集合名称检索mongo
命令中的stdin输入,如上所述 :
... | 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
确保使用双引号传递复杂命令会正确传递给shell。
Shell变量引用是双引号以确保按原样使用。
{1}
/ GNU -q
调用: xargs
和GNU parallel
都支持xargs
(GNU parallel:alias parallel
):
-t
将每个命令行打印到 stderr ,,然后才启动 。--verbose
,输入引用不会反映在打印的命令中,因此您将无法验证指定的参数边界。 -t
示例:
xargs
这会产生类似的结果:
xargs -t
注意:
命令行缺少参数周围的原始引号,但调用仍按原始指定执行。
在命令启动之前立即打印(到stderr)。
如上所述,命令的输出可能无序到达并且不可预测地交错。
总体执行大约需要2.5秒,按以下方式分解:
由于$ 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
和-P 2
命令并行运行,而echo 1; ...
命令则作为 3rd 一个最初被阻止,因为一次允许运行的命令不超过2个。
1秒后,echo 2; ...
命令完成,将并行进程的运行次数降至1,触发执行剩余的echo 3; ...
命令。
因此,因为最后一个命令在1秒后启动并运行了1.5秒,所以最后一个命令在ca之后完成。 2.5秒(前两个命令分别在1秒和2秒后完成)。
GNU echo 1; ...
示例:
echo 3; ...
注意:
由于该命令使用引号来划分参数,并且必须将分界传递给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
- 引用可能看起来不寻常,但它是正确的shell引用,并且完全按照幕后调用的方式反映命令。
GNU -q
期望参数默认为行分隔,因此shell命令使用ANSI C引用的字符串({{1})在各行上传递})\
转义序列。
整体处理时间比parallel
要长,这可能是您为GNU $'...'
的附加功能和技术基础(Perl脚本)支付的 - 可能是微不足道的 - 。
其中一个附加功能是前面提到的输出序列化(分组): 1st 命令的输出首先可预测,即使它和第二个命令已启动同时;在它完成之后才打印第二个命令的输出(这就是第三个命令行的诊断打印首先显示的原因)。
GNU \n
还支持xargs
,仅打印命令 - 到 stdout - 而不实际运行它们。
parallel
答案 2 :(得分:0)
只需在
末尾添加&
即可
for collection in $cols; do
mongodump --host "$h" -q "{_id:{\$gt:$oid}}" -d "$db" -c
"$collection" --out /data/daily/$TODAY/$h
done &