如何使用Python中的mysqldump和mysql复制数据库?

5
我正在编写一个简单的Python脚本来复制MySQL数据库。我试图根据以下SO问题及其答案来复制数据库:“不使用mysqldump复制/复制数据库”,“python subprocess和mysqldump”和“Python subprocess,mysqldump和pipes”。然而,我的脚本由于某种原因无法正常工作,因为表和数据没有出现在我的新数据库中。
从输出中可以看出,mysqldump正常工作(我在输出中看到“Dump completed on...”),因此我认为我的管道有问题
这是我的脚本:
#!/usr/bin/env python

import pymysql
from subprocess import Popen, PIPE, STDOUT

conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='mydb')
cur = conn.cursor()

print("Attempting to create new database...")
try:
    cur.execute("CREATE DATABASE mydb2")
    print("Creating new database")
except Exception:
    print("Database already exists")
print()

# close connection just to be sure
cur.close()
conn.close()

print("Trying to copy old database to new database...")

args1 = ["mysqldump", "-h", "localhost", "-P", "3306", "-u", "root", "-p", "mydb"]
args2 = ["mysql", "-h", "localhost", "-P", "3306", "-u", "root", "-p", "mydb2"]

p1 = Popen(args1, stdout=PIPE, stderr=STDOUT)
p2 = Popen(args1, stdin=p1.stdout, stdout=PIPE, stderr=STDOUT)
output = p2.communicate()

print("output:")
print(output)
print()

您可以看到,我从这个答案中采用了复制数据库管道。一开始,我遇到了错误mysqldump: Couldn't find table: "|",就像那个问题中一样。所以现在我按照建议使用了两个subprocess.Popen调用,解决了该错误信息。

输出变量显示执行了mysqldump,但没有任何关于mysql命令的提及。

我尝试使用p2.wait()p1.wait()代替p2.communicate()中的一个答案,但这只会使我的Python脚本变得无响应。

我还尝试了以下方法:

output1 = p1.communicate()
output2 = p2.communicate()

但是,输出1和输出2都显示相同的mysqldump输出。所以我想这只是一个愚蠢的事情。
我也尝试过使用subprocess.call替代subprocess.Popen,但这也使得我的脚本变得无响应。
在Popen或call中包括shell=True也会导致脚本变得无响应。
然而,在命令提示符中输入以下命令(我使用的是Windows 8.1)可以正常工作:
mysqldump -h localhost -P 3306 -u root -p mydb | mysql -h localhost -P 3306 -u root -p mydb2
它可以在不到三秒钟的时间内复制我的小型测试数据库。
我希望我也能让它在Python中工作。
4个回答

18

我不知道你想使用多纯粹的Python进行复制,但你可以将整个管道操作委托给shell。

subprocess.Popen('mysqldump -h localhost -P 3306 -u -root mydb | mysql -h localhost -P 3306 -u root mydb2', shell=True)

当您在shell上运行时,它应该以同样的方式工作。


可爱。多么奇怪,你不能简单地使用SQL复制数据库:显然这是一个故意的选择,但为什么呢? - mike rodent

5

在我尝试执行同样的任务时,我一直回到这篇文章,并且我意识到你的mysql和mysqldump命令中的"-p"开关是导致无响应的原因。单独使用“-p”表示“提示输入密码”,因此子进程无响应,因为它们正在等待密码输入。

以防其他人遇到这个古老的线程并尝试使其工作,这对我来说是一个绊脚石。


5

我看到的一个问题出现在这行代码上:

p2 = Popen(args1, stdin=p1.stdout, stdout=PIPE, stderr=STDOUT)

应该这样写:

应该这样阅读:

p2 = Popen(args2, stdin=p1.stdout, stdout=PIPE, stderr=STDOUT)

(args1被传递给第二个进程,因此程序执行了两次转储和零次恢复)

3

以下是如何在不使用Shell的情况下运行mysqldump .. | mysql管道:

#!/usr/bin/env python
from subprocess import Popen, PIPE

mysql = Popen("mysql -h localhost -P 3306 -u root -p mydb2".split(),
              stdin=PIPE, stdout=PIPE)
mysqldump = Popen("mysqldump -h localhost -P 3306 -u root -p mydb".split(),
                  stdout=mysql.stdin)
mysql_stdout = mysql.communicate()[0]
mysqldump.wait()

请查看如何使用subprocess.Popen通过管道连接多个进程?

如果您不需要传递需要复杂(可能不可移植)转义的命令行参数、捕获退出状态和标准输出,那么在此处使用shell会更加简单。


1
我不知道我们需要 .wait() :) - matiu
2
@matiu: 调用mysqldump.wait()是为了避免创建僵尸进程。当我们调用.wait()时,mysqldump进程本身已经完成了。 - jfs
我很好奇:使用这种语法与接受的答案中的内联管道相比是否有任何优势?(除了可读性,我猜) - olinox14
1
@olinox14:答案中的最后一段回答了这个问题。 - jfs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接