【问题标题】:python subprocess and mysqldumppython子进程和mysqldump
【发布时间】:2013-07-27 04:51:19
【问题描述】:

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

我正在尝试执行

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 的输出通过管道传输到一个套接字并通过网络触发它并在它到达时保存它,而不是发送一个压缩文件?

谢谢!

【问题讨论】:

标签: python subprocess mysqldump


【解决方案1】:

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

【讨论】:

  • 谢谢。这就是我要找的!
【解决方案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)

【讨论】:

  • 太棒了。我试试看。
  • 您可能需要sendall。而且,关于从 p1 关闭输出管道的好处是,否则 p2 将无法完成......
【解决方案3】:

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

"注意读取的数据是缓存在内存中的,所以不要使用这个方法 如果数据量很大或没有限制。” - 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 中并让它每隔一段时间运行 :)

【讨论】:

  • 情况如下:
  • 我正在将数据收集到系统上的数据库中。当磁盘达到某个阈值时,我想将压缩转储移动到另一个系统,并清除数据库。我认为最好的方法是在同一台机器上转储/压缩。我试图想出一种将转储流式传输到终极计算机的方法,但我想不出一种方法来做到这一点。我读过 os.system 已被弃用,所以我想我会给 subprocess 一个机会。不妨我们 os.system 虽然。这很简单。谢谢。
  • @glglgl 它在 glglgl 中没有被弃用;D。还有 Zobal,您可能想尝试 bash 脚本而不是 python 脚本。如果它们在同一台计算机上,则不需要使用管道。这些是用于进程间信号的,然后是传输大量数据
【解决方案4】:

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

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

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

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

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

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

由于p2 的输出都没有发送到PIPE,因此这里无需调用p2.communicate():您只需调用p2.wait()。这更清楚地表明没有数据从p2 流回(我会说这是对代码的一个小改进,尽管如果你决定要捕获p2 的stderr 毕竟,你必须改变那个背)。


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

【讨论】:

    猜你喜欢
    • 2011-04-05
    • 1970-01-01
    • 1970-01-01
    • 2010-10-30
    • 2012-01-10
    • 2021-11-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-22
    相关资源
    最近更新 更多