2017-02-03 41 views
1

我正在研究從本地驅動器讀取DBF文件並將數據加載到sql服務器表的程序。我對Python很綠,我發現了一些關於多線程的細節,其中大部分都是令人困惑的。讀取和插入的性能很慢,看我的CPU使用率,我有足夠的容量。我也在運行SSD。Python - 多線程幫助 - 讀取多個文件 - ETL到SQL服務器

此代碼將被擴展到大約400個拉鍊之間的大約20個DBF文件中。所以我們總共討論了8000個DBF文件。

我很難做到這一點。你能提供指針嗎?

這裏是我的代碼(這是一個有點混亂,但以後我會清理),

import os, pyodbc, datetime, shutil 
from dbfread import DBF 
from zipfile import ZipFile 

# SQL Server Connection Test 
cnxn = pyodbc.connect('DRIVER={SQL Server};SERVER=localhost\test;DATABASE=TEST_DBFIMPORT;UID=test;PWD=test') 
cursor = cnxn.cursor() 

dr = 'e:\\Backups\\dbf\\' 
work = 'e:\\Backups\\work\\' 
archive = 'e:\\Backups\\archive\\' 


for r in os.listdir(dr): 

    curdate = datetime.datetime.now() 
    filepath = dr + r 
    process = work + r 
    arc = archive + r 

    pth = r.replace(".sss","") 
    zipfolder = work + pth 
    filedateunix = os.path.getctime(filepath) 
    filedateconverted=datetime.datetime.fromtimestamp(int(filedateunix) 
               ).strftime('%Y-%m-%d %H:%M:%S') 
    shutil.move(filepath,process) 
    with ZipFile(process) as zf: 
     zf.extractall(zipfolder) 


    cursor.execute(
     "insert into tblBackups(backupname, filedate, dateadded) values(?,?,?)", 
    pth, filedateconverted, curdate) 
    cnxn.commit() 

    for dirpath, subdirs, files in os.walk (zipfolder): 

     for file in files: 
      dateadded = datetime.datetime.now() 

      if file.endswith(('.dbf','.DBF')): 
       dbflocation = os.path.abspath(os.path.join(dirpath,file)).lower() 

       if dbflocation.__contains__("\\bk.dbf"): 
        table = DBF(dbflocation, lowernames=True, char_decode_errors='ignore') 
        for record in table.records: 
         rec1 = str(record['code']) 
         rec2 = str(record['name']) 
         rec3 = str(record['addr1']) 
         rec4 = str(record['addr2']) 
         rec5 = str(record['city']) 
         rec6 = str(record['state']) 
         rec7 = str(record['zip']) 
         rec8 = str(record['tel']) 
         rec9 = str(record['fax']) 
         cursor.execute(
         "insert into tblbk(code,name,addr1,addr2,city,state,zip,tel,fax) values(?,?,?,?,?,?,?,?,?)", 
         rec1, rec2, rec3, rec4, rec5, rec6, rec7, rec8, rec9, rec10, rec11, rec12, rec13) 
       cnxn.commit() 


       if dbflocation.__contains__("\\cr.dbf"): 
        table = DBF(dbflocation, lowernames=True, char_decode_errors='ignore') 
        for record in table.records: 
         rec2 = str(record['cal_desc']) 
         rec3 = str(record['b_date']) 
         rec4 = str(record['b_time']) 
         rec5 = str(record['e_time']) 
         rec6 = str(record['with_desc']) 
         rec7 = str(record['recuruntil']) 
         rec8 = record['notes'] 
         rec9 = dateadded 
         cursor.execute(
         "insert into tblcalendar(cal_desc,b_date,b_time,e_time,with_desc,recuruntil,notes,dateadded) values(?,?,?,?,?,?,?,?)", 
         rec2, rec3, rec4, rec5, rec6, rec7, rec8, rec9) 
       cnxn.commit() 

    shutil.move(process, archive) 
    shutil.rmtree(zipfolder) 
+0

我想要的另一個選擇是可能更簡單的多處理。 – HMan06

回答

1

TL;博士:測量的,後來修復!


注意,在最常用的Python實現(CPython的)只有一個線程在同一時間可以執行Python字節碼。 因此,線程不是提高CPU限制性能的好方法。如果工作是I/O限制的,他們可以很好地工作。

但你應該首先做的是措施。這不能夠強調。如果你不知道是什麼導致缺乏表現,你不能修復它!

編寫完成該任務的單線程代碼,然後在分析器下運行該代碼。首先嚐試內置cProfile。如果這不能爲您提供足夠的信息,請嘗試一個line profiler

分析應該告訴你哪些步驟消耗的時間最多。一旦你知道了,你可以開始改進。

例如,使用multiprocessing來讀取DBF文件是沒有意義的,如果這是將數據填充到SQL服務器中的操作花費最多的時間!這甚至可能會減慢速度,因爲有幾個進程正在爭奪SQL服務器的注意力。

如果 SQL服務器不是瓶頸,它可以處理多個連接,我會用multiprocessing,大概Pool.map()閱讀DBF的並行和數據填充到SQL服務器。在這種情況下,您應該在DBF文件名列表上覆蓋Pool.map,以便在工作進程中打開這些文件。

+0

謝謝羅蘭,你對這個完全正確。除了我的代碼外,沒有任何瓶頸。我能夠使用多處理模塊,並以1秒的延遲一次加載15個進程。我這樣做是因爲我注意到有時這些進程試圖獲取相同的文件。現在我遇到了SQL Server CPU瓶頸,這正是我想看到的。 SQL Server CPU使用率從大約8%上升到50%,處理8k文件的時間從10多個小時增加到45分鐘! – HMan06

+0

@ HMan06速度超過10倍?太好了! –