4

我知道这个问题的一部分之前已经被问过,但我有一些相关的问题。

我正在尝试执行

mysqldump -u uname -ppassword --add-drop-database --databases databaseName | gzip > fileName

我可能会转储一个非常大的(200GB?)数据库。这本身就是一件愚蠢的事情吗?然后我想通过网络发送压缩文件进行存储,删除本地转储,并清除几个表。

无论如何,我正在使用这样的子进程,因为似乎没有办法在不考虑子进程的情况下执行整个原始调用 | 成为表名。:

from subprocess import Popen, PIPE

f = open(FILENAME, 'wb')
args = ['mysqldump', '-u', 'UNAME', '-pPASSWORD', '--add-drop-database', '--databases', 'DB']

p1 = Popen(args, stdout=PIPE)
P2 = Popen('gzip', stdin=p1.stdout, stdout=f)
p2.communicate()

但后来我读到通信将数据缓存在内存中,这对我不起作用。这是真的?

我现在最终做的是:

import gzip
subprocess.call(args, stdout=f)
f.close()

f = open(filename, 'rb')
zipFilename = filename + '.gz'
f2 = gzip.open(zipFilename, 'wb')
f2.writelines(f)
f2.close()
f.close()

当然这需要一百万年,我讨厌它。

我的问题: 1. 我可以在非常大的数据库上使用我的第一种方法吗?2. 我是否可以将 mysqldump 的输出通过管道传输到一个套接字并通过网络触发它并在它到达时保存它,而不是发送一个压缩文件?

谢谢!

4

4 回答 4

8

你不需要通信()。如果您想阅读 stdout/stderr 以完成,它只是一种方便的方法。但是由于您正在链接命令,因此它们正在为您执行此操作。只等他们完成。

from subprocess import Popen, PIPE

args = ['mysqldump', '-u', 'UNAME', '-pPASSWORD', '--add-drop-database', '--databases', 'DB']

with open(FILENAME, 'wb', 0) as f:
    p1 = Popen(args, stdout=PIPE)
    p2 = Popen('gzip', stdin=p1.stdout, stdout=f)
p1.stdout.close() # force write error (/SIGPIPE) if p2 dies
p2.wait()
p1.wait()
于 2013-07-26T20:22:22.550 回答
3

你离你想要的地方很近:

from subprocess import Popen, PIPE

f = open(FILENAME, 'wb')
args = ['mysqldump', '-u', 'UNAME', '-pPASSWORD', '--add-drop-database', '--databases', 'DB']

p1 = Popen(args, stdout=PIPE)

到这里为止是对的。

p2 = Popen('gzip', stdin=p1.stdout, stdout=PIPE)

这个接受p1' 的输出并对其进行处理。之后我们可以(并且应该)立即p1.stdout.close()

现在我们有了一个p2.stdout可以读取的文件,并且不使用临时文件,通过网络发送它:

s = socket.create_connection(('remote_pc', port))
while True:
    r = p2.stdout.read(65536)
    if not r: break
    s.send(r)
于 2013-07-26T20:37:29.080 回答
2

是的,数据缓冲在内存中:

“注意读取的数据是缓存在内存中的,所以如果数据量很大或者没有限制,不要使用这个方法。” -子流程文档

不幸的是,目前没有办法异步使用 Popen:PEP3145

而不是在python中做这一切,你可以手动做

os.system("mysqldump -u uname -ppassword --add-drop-database --databases databaseName | gzip > fileName

")

当然使用适当的字符串替换string.format;否则你会给你的电脑带来不必要的压力,尤其是试图通过管道通信 200gb ......

你能详细说明你想要做什么吗?现在听起来你在同一台计算机上同时倾倒和压缩。


是的,您可以通过网络流式传输文件.. 我不知道您是否想直接流式传输 mysql 的输出 - 您可能想在考虑之前先查看您的网络功能


重击:

#!/bin/bash
mysqldump -u uname -ppassword --add-drop-database --databases databaseName | gzip > fileName
 #transfer fileName to other computer

^ 你也可以把它放在一个 crontab 中,让它每隔一段时间运行 :)

于 2013-07-26T19:36:12.943 回答
2

您使用两个subprocess.Popen调用的示例代码是正确的(尽管可以稍微改进),并且:

...我读到通信将数据缓存在内存中

也是正确的——它将“通信命令”在 a 上产生的所有标准输出和标准错误输出读入内存subprocess.PIPE——但在这里不是问题,因为你有这个:

p1 = Popen(args, stdout=PIPE)
P2 = Popen('gzip', stdin=p1.stdout, stdout=f)
p2.communicate()

您正在调用communicate()p2其 stdout 输出被发送到f(打开的文件),并且其 stderr 输出——无论如何可能是空的(没有发生错误)——没有被发送到PIPE. 因此,p2.communicate()最坏的情况是必须读取和缓冲总共零字节的 stdout 加上零字节的 stderr。它实际上更聪明一点,注意到没有PIPE,所以它返回元组(None, None)

如果您要调用p1.communicate(),那将是一个更大的问题(尽管在这种情况下,您将与p2gzip 进程争夺来自 的输出p1,这会更糟)。但你不是;p1的输出流向p2p2的输出流向文件。

由于没有p2's 的输出被发送到 a PIPE,所以这里没有必要调用p2.communicate():你可以简单地调用p2.wait(). 这更清楚地表明没有数据流回p2(我会说这是对代码的一个小改进,尽管如果你决定要捕获p2's stderr ,你必须把它改回来)。


编辑添加:就像glglgl的回答一样,在创建p2之后关闭p1管道很重要,否则也会等待您的Python进程将数据发送到。p2p2p2

于 2013-07-26T20:22:30.923 回答