2013-07-26 4 views
2

Я знаю, что части этого вопроса заданы раньше, но у меня есть некоторые связанные вопросы.python subprocess и mysqldump

Я пытаюсь выполнить

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

Я потенциально демпинг очень большой (200GB?) Дб. Это само по себе глупое дело? Затем я хочу отправить zip-файл по сети для хранения, удалить локальный дамп и очистить пару таблиц.

Во всяком случае, я использовал такой подпроцесс, потому что, похоже, не существует способа выполнить весь исходный вызов без подпроцесса с учетом | быть имя таблицы .:

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. Могу ли я использовать свой первый подход на очень большом db? 2. Могу ли я подключить вывод mysqldump к сокету и запустить его по сети и сохранить его, когда он поступит, вместо отправки zip-файла?

Спасибо!

+0

связан: [Как использовать subprocess.Popen для подключения нескольких процессов по трубам ?] (http://stackoverflow.com/q/295459/4279) – jfs

ответ

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() 
+0

Спасибо. Это то, что я ищу! – Zobal

1

Йуп данных в буфер в памяти:

«Примечание Считанные данные, буферизованные в памяти, так что не использовать этот метод , если размер данных является большим или неограниченным.» - subprocess docs

К сожалению, на данный момент не существует никакого способа асинхронно использовать Popen: PEP3145

Вместо того, чтобы делать все это в Python вы можете вручную сделать

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

«)

с соответствующими заменами строк, используя, конечно, string.format, в противном случае вы ставите ненужный стресс на свой компьютер, особенно пытаясь сообщить 200 гб по трубе ...

Можете ли вы подробнее рассказать о том, что вы пытаетесь сделать? Прямо сейчас звучит так, будто вы демпинг и запинка на одном компьютере.


Да, вы можете передавать файл по сети.Я не знаю, если вы хотите, чтобы непосредственно поток вывода MySQL напрямую, хотя - вы можете захотеть взглянуть на свои возможности сети перед тем, учитывая, что


Баш:

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

^вы можете также положите это в crontab и запустите его с интервалами :)

+0

Вот что: – Zobal

+0

Пожалуйста, не 'os.system()' ... – glglgl

+0

Я собираю данные в базу данных в системе. Когда диск достигает определенного порога, я хочу переместить сгиб-дамп в другую систему и очистить db. Я думаю, что лучший способ сделать это - dump/zip на той же машине. Я пытался придумать способ потоковой передачи дампа на конечный компьютер, но я не могу придумать, как это сделать. Я читал, что os.system устарела, поэтому я подумал, что я дам подпроцесс выстрел. Возможно, мы тоже os.system. Это достаточно прямо вперед. Благодарю. – Zobal

2

Ваш пример кода с использованием двух subprocess.Popen вызовов является правильным (хотя и слегка улучшить-состоянии), и это:

... Я читал, что общаться кэширует данные в памяти

также является правильным - он считывает в память все стандартные выходные и стандартные ошибки-выходы, которые «команда связи» производит на subprocess.PIPE, но здесь не проблема, потому что у вас есть это:

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

Вы звоните communicate() на p2, чей стандартный вывод вывод направляется f (открытый файл), и чей STDERR выход, который, вероятно, пусто в любом случае (не возникает никаких ошибок) - не отправляется на номер PIPE. Таким образом, p2.communicate() в худшем случае должны были бы считывать и накапливать большое количество нулевых байтов stdout плюс нулевые байты stderr. Это на самом деле немного более умно, заметив, что нет PIPE, поэтому он возвращает кортеж (None, None).

Если вы были вызвать p1.communicate(), что будет больше проблем (хотя и в этом случае вы бы тогда бороться с p2, процессом GZIP, для выхода из p1, что было бы еще хуже). Но вы не; p1 поступает в выходной файл p2 и p2, который поступает в файл.

Поскольку ни один выход p2 «s не отправляется в PIPE, нет необходимости вызывать p2.communicate() здесь: вы можете просто позвонить p2.wait(). Это делает более ясным, что нет данных, возвращающихся с p2 (что я бы сказал, это небольшое улучшение кода, хотя, если вы решите, что хотите снять stderr , вам придется изменить его).


Изменить, чтобы добавить: как в ответ glglgl, это важно, чтобы закрыть p1 «s трубу к p2 после создания p2, в противном случае p2 будет ждать ваш процесс Python для отправки данных p2 тоже.

+0

Большое спасибо. Это был очень информативный ответ. – Zobal

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 «s и обрабатывает его. Впоследствии мы можем (и должны) немедленно 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) 
+0

Удивительный. Я сделаю это. – Zobal

+1

Возможно, вам понадобится 'sendall'. И, хороший момент о закрытии выходной трубы от p1, иначе p2 не закончится ... – torek

+0

@torek Вы правы в 'sendall()' ... – glglgl