2014-05-14 21 views
1

社區早上好,我有一個查詢,你碰巧必須導入1400萬條記錄,其中包含公司客戶的信息。如何優化從平面文件到BD PostgreSQL的數據導入?

平面文件。 Txt的重量爲2.8GB,我開發了一個java程序,它逐行讀取平面文件,處理信息並將其放入一個對象中,該對象又插入到PostgreSQL數據庫的表中,該主題是我已經制作的計算在100分鐘的時間內插入了100000條記錄,但問題在於我插入了部分內容。

public static void main(String[] args) { 

    // PROCESSING 100,000 records in 112 minutes 
  // PROCESSING 1,000,000 records in 770 minutes = 18.66 hours 

    loadData(0L, 0L, 100000L); 
} 

/** 
* Load the number of records Depending on the input parameters. 
* @param counterInitial - Initial counter, type long. 
* @param loadInitial - Initial load, type long. 
* @param loadLimit - Load limit, type long. 
*/ 
private static void loadData(long counterInitial, long loadInitial, long loadLimit){ 
    Session session = HibernateUtil.getSessionFactory().openSession(); 
    try{ 
     FileInputStream fstream = new FileInputStream("C:\\sppadron.txt"); 
     DataInputStream entrada = new DataInputStream(fstream); 
     BufferedReader buffer = new BufferedReader(new InputStreamReader(entrada)); 
     String strLinea; 
     while ((strLinea = buffer.readLine()) != null){ 
      if(counterInitial > loadInitial){ 
       if(counterInitial > loadLimit){ 
        break; 
       } 
       Sppadron spadron= new Sppadron(); 
       spadron.setSpId(counterInitial);     
       spadron.setSpNle(strLinea.substring(0, 9).trim()); 
       spadron.setSpLib(strLinea.substring(9, 16).trim()); 
       spadron.setSpDep(strLinea.substring(16, 19).trim()); 
       spadron.setSpPrv(strLinea.substring(19, 22).trim()); 
       spadron.setSpDst(strLinea.substring(22, 25).trim()); 
       spadron.setSpApp(strLinea.substring(25, 66).trim()); 
       spadron.setSpApm(strLinea.substring(66, 107).trim()); 
       spadron.setSpNom(strLinea.substring(107, 143).trim());     
       String cadenaGriSecDoc = strLinea.substring(143, strLinea.length()).trim();      
       String[] tokensVal = cadenaGriSecDoc.split("\\s+"); 
       if(tokensVal.length == 5){ 
       spadron.setSpNac(tokensVal[0]); 
       spadron.setSpSex(tokensVal[1]); 
       spadron.setSpGri(tokensVal[2]); 
        spadron.setSpSec(tokensVal[3]); 
        spadron.setSpDoc(tokensVal[4]); 
       }else{ 
       spadron.setSpNac(tokensVal[0]); 
       spadron.setSpSex(tokensVal[1]); 
       spadron.setSpGri(tokensVal[2]); 
        spadron.setSpSec(null); 
        spadron.setSpDoc(tokensVal[3]); 
       } 
       try{ 
        session.getTransaction().begin(); 
        session.save(spadron); // Insert 
        session.getTransaction().commit(); 
       } catch (Exception e) { 
        session.getTransaction().rollback(); 
        e.printStackTrace(); 
       } 
      } 
      counterInitial++; 
     } 
     entrada.close(); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    }finally{ 
     session.close(); 
    } 
} 

的主要問題是,如果他們檢查我的代碼時,我插入第一個百萬條記錄,參數情況如下:loadData (0L, 0L, 1000000L);

的問題是,當你在這種情況下,插入下面的記錄將是下一個百萬記錄將是:loadData (0L, 1000000L, 2000000L); 什麼會導致它回滾前1000億記錄,然後當計數器在值1000001最近將開始插入以下記錄,有人可以給我一個更優化的建議插入記錄,知道有必要對待前面顯示的代碼中看到的信息。

+2

您正在提交每個插入。 *會變得非常緩慢。你應該只在最後提交。或者爲此停止使用Hibernate,它不是爲批量加載而設計的。最有效的解決方案是通過'CopyManager' API使用'copy'。或者至少使用普通的JDBC和語句批處理。 –

回答

0

請參閱How to speed up insertion performance in PostgreSQL

你應該做的第一件事就是繞過Hibernate。 ORMs很方便,但爲了方便起見,你要付出代價,特別是在批量操作方面。

您可以使用JDBC PreparedStatement將您的插入組合到合理大小的事務中,並使用多值insert s。

個人而言,我會use PgJDBC's support for the COPY protocol做更直接的插入。打開Hibernate Session對象以獲取底層的java.sql.Connection,get the PGconnection interface for it,getCopyAPI()以獲取CopyManager,並使用copyIn將數據提供給數據庫。

由於您的數據看起來不像CSV格式,而是固定寬度的字段格式,因此您需要做的是啓動一個線程,該線程從文件中讀取數據,將每個數據轉換爲適合PostgreSQL輸入,並將其寫入緩衝區,copyIn可以使用傳遞的Reader消耗。這聽起來比它更復雜,並且有很多使用java.io.Readerjava.io.Writer接口的Java生產者/消費者線程實現的例子。

您可以改爲編寫一個過濾器Reader包裝底層的文件讀取器和變換每一行。這比生產者/消費者線程要簡單得多。首先研究它作爲首選。

相關問題