2013-07-26 61 views
2

我知道這個問題的一部分已經被問過,但我有一些相關的問題。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的輸出傳輸到套接字並在網絡上觸發它並在到達時保存它,而不是發送壓縮文件?

謝謝!

+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;否則你會給計算機帶來不必要的壓力,特別是試圖通過管道溝通200GB ...

你能詳細說明你正在嘗試做什麼嗎?現在,這聽起來像是你在同一臺計算機上傾倒和壓縮。


是的,你可以通過網絡流文件。我不知道,如果你想直接流的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

我收集數據到系統的數據庫。當磁盤達到某個閾值時,我想將壓縮轉儲移到另一個系統,然後清除數據庫。我認爲最好的方法是在同一臺機器上進行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()最壞的情況是不得不讀取並緩衝總共0字節的標準輸出加零字節的標準錯誤。它實際上更聰明一些,注意到沒有PIPE,所以它返回元組(None, None)

如果你打電話給p1.communicate(),那將是更大的問題(雖然在這種情況下,你再與p2,gzip的過程中戰鬥,從p1輸出,這將是更糟糕)。但你不是; p1的輸出流向p2,並且p2的輸出流向文件。

由於沒有p2的輸出發送到PIPE,這裏沒有必要撥打p2.communicate():您可以簡單地撥打p2.wait()。這更清楚地表明沒有數據從p2(我認爲這是對代碼的小改進,儘管如果你決定要捕獲p2的stderr畢竟,你必須改變它)。


編輯補充:在glglgl的答案,它創造P2後關閉p1的管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的輸出和處理它。之後我們可以(也應該)立即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