python子进程和mysqldump

时间:2013-07-26 19:28:16

标签: python subprocess mysqldump

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

我正在尝试执行

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 个答案:

答案 0 :(得分:5)

您不需要沟通()。如果你想读取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()

答案 1 :(得分:2)

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

  

...我读过,通信缓存内存中的数据

也是正确的 - 它将“通信命令”在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(),这将是一个更大的问题(尽管在这种情况下,你将与p2(gzip进程)争夺{{1的输出,这会更糟糕)。但你不是; p1的输出流向p1p2的输出流向文件。

由于p2的输出都没有发送到p2,因此无需在此处拨打PIPE:您只需拨打p2.communicate()即可。这使得更清楚的是没有数据从p2.wait()流回来(我会说这是对代码的一个小改进,尽管如果你决定想要捕获p2的stderr,那么你' d必须改变它。)


编辑添加:在glglgl的答案中,在创建p2后关闭p2的管道到p1非常重要,否则p2会等待你的Python进程发送数据到p2也是。

答案 2 :(得分:2)

你非常接近你想要的地方:

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)

答案 3 :(得分:1)

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

  

“注意读取的数据缓冲在内存中,因此请勿使用此方法   如果数据量很大或无限制。“ - subprocess docs

不幸的是,目前没有办法异步使用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中并让它间隔运行:)