應用場景:本人需要對200萬條網頁html格式數據進行清洗,提取文字后將分詞結果寫入數據庫,之前做了一次,大概花費了80多個小時才跑完。機器配置是4核,內存8G;開完會領導讓再改點東西重新跑一遍,然后說用多進程或者多線程跑,速度快。
本人接觸python不到兩個月,以前也基本不怎么編程,所以這種優化什么的東西都不懂,只好求助同事。同事告訴我直接看廖雪峰的教程就ok了。先看了一下廖雪峰寫的單個進程代碼,比較容易懂,但是我需要開四個進程,把我的cpu都占滿來跑,這樣效率才最大。
所以又看了多進程的例子,並自己實踐了一下:
from multiprocessing import Process import os from multiprocessing import Pool def run_proc(i): a=i*3 print(a) if __name__=='__main__': p = Pool(4) for i in range(5): p.apply_async(run_proc, args=(i,)) p.close() p.join()
看一下上面代碼,Pool代表的是一個進程池,里面寫幾就代表你想跑幾個進程,但是你的cpu是幾核你就只能開幾個進程,而且進程數最好是2的整數倍(同事告訴我的)。查看cpu核數的方法就是打開任務管理器,然后性能里面有幾個小窗口就代表幾核。
我的是四個小窗口,代表四核。按照上面的例子我用四個核來計算run_proc這個函數,然后每個核計算的是一個i值對應的run_proc函數。
接下來到我的實際場景中,我是想從數據庫中讀取200萬條數據並對所有數據進行一系列操作后再寫如數據庫,這里我采用四核全跑,每個核分配50萬數據來進行:
start = datetime.datetime.now() p = Pool(4) # 建立進程池 get_data_cmd=['SELECT * FROM 標訊樣本_二百萬 limit 0,500000', 'SELECT * FROM 標訊樣本_二百萬 limit 500000,500000', 'SELECT * FROM 標訊樣本_二百萬 limit 1000000,500000', 'SELECT * FROM 標訊樣本_二百萬 limit 1500000,500000' ] for cmd in get_data_cmd: # 將每個命令傳入不同的子進程執行相同的代碼 p.apply_async(con_seg_word, args=(cmd,)) p.close() p.join()
我的方法比較笨,就是將四個不同的sql命令放在一個列表中,然后循環讀取列表中的語句來分配給每個核要跑的函數,這樣就ok了,時間大約比之前少了4倍吧。
apply_async這個方法就是你要將你的這四個sql命令給哪個函數,其中arg就是要傳遞進函數的參數。
PS:這個多進程的方法其實有很多隱藏的問題在里面的,例如針對limit這個問題,后面會越來越慢,因為limit相當於全表進行過濾,因此不推薦此種方式;
另外在innodb這種引擎數據庫中,當limit幾千萬數據后面的數據時候會報錯,(the totle number of locks exceeds the lock table size),這是因為InnoDB表執行大批量數據的更新,插入,刪除操作時會出現這個問題,需要調整InnoDB全局的innodb_buffer_pool_size的值來解決這個問題,並且重啟mysql服務。
這里還需要進一步來探索多進程的內部機制從而來寫一個更好的多進程栗子。