subproceso python y mysqldump

Sé que partes de esta pregunta se han preguntado anteriormente, pero tengo algunas preguntas relacionadas.

Estoy tratando de ejecutar

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

Estoy potencialmente descargando un db muy grande (200GB?) ¿Es eso en sí una cosa tonta que hacer? Luego quiero enviar el archivo comprimido a través de la red para su almacenamiento, eliminar el volcado local y purgar un par de tablas.

De todos modos, estaba usando un subproceso como este, porque no parece haber una manera de ejecutar toda la llamada original sin considerar el subproceso | para ser un nombre de tabla .:

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

pero luego leí que la comunicación almacena en caché los datos en la memoria, lo que no funcionaría para mí. ¿Es esto cierto?

Lo que terminé haciendo por ahora es:

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

Por supuesto, esto lleva un millón de años, y lo odio.

Mis preguntas: 1. ¿Puedo usar mi primer enfoque en un db muy grande? 2. ¿Podría posiblemente canalizar la salida de mysqldump a un socket y dispararlo a través de la red y guardarlo cuando llegue, en lugar de enviar un archivo comprimido?

¡Gracias!

No necesitas comunicarte (). Solo existe como un método conveniente si desea leer stdout / stderr hasta su finalización. Pero como estás encadenando los comandos, ellos están haciendo eso por ti. Solo espera a que se completen.

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

Estás bastante cerca de donde quieres:

 from subprocess import Popen, PIPE f = open(FILENAME, 'wb') args = ['mysqldump', '-u', 'UNAME', '-pPASSWORD', '--add-drop-database', '--databases', 'DB'] p1 = Popen(args, stdout=PIPE) 

Hasta aquí está bien.

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

Éste toma la salida de p1 y la procesa. Luego podemos (y debemos) inmediatamente p1.stdout.close() .

Ahora tenemos una p2.stdout cual se puede leer y, sin usar un archivo temporal, enviarlo a través de la red:

 s = socket.create_connection(('remote_pc', port)) while True: r = p2.stdout.read(65536) if not r: break s.send(r) 

Su código de ejemplo que utiliza dos subprocess.Popen calls es correcto (aunque ligeramente mejorable), y esto:

… leí que comunican en caché los datos en memoria

también es correcto: lee en la memoria toda la salida estándar y la salida estándar-error que el “comando de comunicación” produce en un subprocess.PIPE PIPE: pero no hay problema aquí , porque tiene esto:

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

Está llamando a communicate() en p2 , cuya salida estándar se envía a f (un archivo abierto), y cuya salida stderr, que probablemente esté vacía de todos modos (no se producen errores), no se envía a un PIPE . Por lo tanto, p2.communicate() en el peor de los casos tendría que leer y almacenar un gran total de cero bytes de stdout más cero bytes de stderr. En realidad, es un poco más inteligente, al notar que no hay PIPE , por lo que devuelve la tupla (None, None) .

Si llamaras p1.communicate() , eso sería más que un problema (aunque en este caso estarías luchando con p2 , el proceso gzip, para la salida de p1 , que sería aún peor). Pero no lo eres; La salida de p1 fluye a p2 , y la salida de p2 fluye a un archivo.

Como ninguna de las salidas de p2 se envía a un PIPE , no es necesario llamar a p2.communicate() aquí: simplemente puede llamar a p2.wait() . Eso deja claro que no hay datos que fluyan de vuelta desde p2 (lo que yo diría que es una mejora menor del código, aunque si decide que desea capturar el stderr de p2 , después de todo, tendrá que volver a cambiarlo).


Editar para agregar: como en la respuesta de glglgl, es importante cerrar la tubería de p1 a p2 después de crear p2, de lo contrario p2 esperará a que el proceso de Python envíe datos a p2 también.

Sí, los datos se almacenan en la memoria intermedia:

“Nota La lectura de los datos se almacena en la memoria intermedia, por lo que no utilice este método si el tamaño de los datos es grande o ilimitado”. – documentos de subproceso

Desafortunadamente en este momento no hay manera de usar Popen de forma asíncrona: PEP3145

En lugar de hacer todo esto en python, puedes hacerlo manualmente.

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

“)

con los reemplazos de cadena apropiados usando string.format por supuesto; de lo contrario, está poniendo una cantidad innecesaria de estrés en su computadora, especialmente tratando de comunicar 200 gb a través de una tubería …

¿Puedes explicar qué estás tratando de hacer? En este momento, parece que estás descargando y comprimiendo en la misma computadora.


Sí, puede transmitir un archivo a través de la red. Sin embargo, no sé si desea transmitir directamente la salida de mysql directamente; es posible que desee ver las capacidades de su red antes de considerar eso.


golpetazo:

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

^ También puedes poner esto en un crontab y hacer que se ejecute a intervalos 🙂