簡體   English   中英

MySQL使用Java從文件插入大型數據集

[英]MySQL Inserting large data sets from file with Java

我需要從CSV文件中將大約180萬行插入到MySQL數據庫中。 (只有一張桌子)

目前使用Java來解析文件並插入每一行。

你可以想象這需要花費幾個小時才能運行。 (10)

我之所以沒有將它直接從文件傳輸到數據庫中,是因為數據必須在將數據添加到數據庫之前進行操作。

這個過程需要由那里的IT經理來運行。 所以我把它設置為一個很好的批處理文件,讓它們在將新的csv文件放到正確的位置后運行。 所以,我需要通過將文件放到某個位置並運行批處理文件來很好地完成這項工作。 (Windows環境)

我的問題是,插入這么多數據的最快方法是什么? 大型插入,來自臨時解析文件或一次插入一次? 可能還有其他想法嗎?

第二個問題是,如何優化我的MySQL安裝以允許非常快速的插入。 (還有一點需要大量選擇所有數據)

注意:該表最終將被刪除,整個過程將在以后再次運行。

一些澄清:目前使用... opencsv.CSVReader解析文件,然后在每一行上插入。 我正在總結一些專欄而忽略其他專欄。

更多說明:本地DB MyISAM表

快速插入提示:

  • 使用LOAD DATA INFILE語法讓MySQL解析並插入它,即使你必須修改它並在操作后提供它。
  • 使用此插入語法:

    插入表(col1,col2)值(val1,val2),(val3,val4),...

  • 在插入之前刪除所有鍵/索引。

  • 在你所擁有的最快的機器中完成它(主要是IO,但RAM和CPU也很重要)。 數據庫服務器,還有插入客戶端,記住你將支付兩倍的IO價格(一次讀取,第二次插入)

我可能會選擇一個很大的數字,比如10k行,然后從CSV加載那么多行,按下數據,然后進行批量更新,然后重復直到你完成了整個csv。 根據數據的按摩/數量,1.8 mil的行不應該花費10個小時,更多的是1-2個小時,具體取決於您的硬件。

編輯:whoops,遺漏了一個相當重要的部分,你的con必須將autocommit設置為false,我復制它的代碼是作為GetConnection()方法的一部分。

    Connection con = GetConnection();
con.setAutoCommit(false);
            try{
                PreparedStatement ps = con.prepareStatement("INSERT INTO table(col1, col2) VALUES(?, ?)");
                try{
                    for(Data d : massagedData){
                        ps.setString(1, d.whatever());
                                        ps.setString(2, d.whatever2());
                                            ps.addBatch();
                    }
                    ps.executeBatch();
                }finally{
                    ps.close();
                }
            }finally{
                con.close();
            }

您是否絕對禁止在JDBC驅動程序中禁用自動提交?

這是JDBC客戶端的典型性能殺手。

你真的應該在MySQL控制台上使用LOAD DATA來實現這一點,而不是通過代碼...

LOAD DATA INFILE 'data.txt' INTO TABLE db2.my_table;

如果你需要操作數據,我仍然建議在內存中操作,重寫為平面文件,並使用LOAD DATA將其推送到數據庫,我認為它應該更有效。

另一個想法是:您是否使用PreparedStatement通過JDBC插入數據?

根據您在插入數據之前需要對數據做些什么,您在速度方面的最佳選擇是:

  • 在java中解析文件/使用數據執行所需操作/將“按摩”數據寫入新的CSV文件/使用“load data infile”。
  • 如果您的數據操作是有條件的(例如,您需要檢查記錄是否存在並根據它是插入還是更新等來執行不同的操作......)那么(1)可能是不可能的。 在這種情況下,您最好進行批量插入/更新。
    嘗試找到適合您的最佳批量大小(從大約500-1000開始應該沒問題)。 根據您用於表的存儲引擎,您可能需要將其拆分為多個事務 - 具有單個跨度1.8M行不會對性能產生奇跡。
  • 您最大的性能問題很可能不是java而是mysql,特別是您插入的表上的任何索引,約束和外鍵。 在開始插入之前,請確保禁用它們。 在最后重新啟用它們將花費相當多的時間,但它比在每個語句之后讓數據庫評估它們更有效。

    由於事務的大小,您可能還會看到mysql性能問題。 您的事務日志將隨着許多插入而變得非常大,因此在X次插入(例如10,000-100,000)之后執行提交也將有助於插入速度。

    從jdbc層,確保在PreparedStatement而不是普通的executeUpdate()上使用addBatch()和executeBatch()命令。

    您可以通過其Connector J JDBC驅動程序中的批處理功能來提高MySQL / Java的批量INSERT性能。

    MySQL沒有“正確”處理批處理(參見我的文章鏈接,底部),但它可以重寫INSERT以利用奇怪的MySQL語法,例如,您可以告訴驅動程序重寫兩個INSERT:

    INSERT INTO (val1, val2) VALUES ('val1', 'val2'); 
    INSERT INTO (val1, val2) VALUES ('val3', 'val4');
    

    作為單一聲明:

    INSERT INTO (val1, val2) VALUES ('val1', 'val2'), ('val3','val4'); 
    

    (請注意,我並不是說需要以這種方式重寫SQL; 驅動程序可以這樣做)

    我們這樣做是為了我們自己的批量插入調查:它產生了一個數量級的差異。 與其他人提到的顯式交易一起使用,您將看到總體上有很大改進。

    相關的驅動程序屬性設置為:

    jdbc:mysql:///<dbname>?rewriteBatchedStatements=true
    

    請參閱: 使用MySQL Connector / J批量INSERT的性能提升10倍

    如果你使用LOAD DATA INFILE而不是插入每一行,會不會更快?

    我會跑三個線程......

    1)讀取輸入文件並將每一行推入轉換隊列2)從隊列中彈出,轉換數據,並推入db隊列3)從db隊列彈出並插入數據

    通過這種方式,您可以在db線程等待其IO完成時從磁盤讀取數據,反之亦然

    如果你還沒有,請嘗試使用MyISAM表類型,只需確保在你做之前閱讀它的缺點。 它通常比其他類型的表更快。

    如果您的表具有索引,則刪除它們通常會更快,然后在導入后將其添加回來。

    如果您的數據都是字符串,但更適合作為關系數據庫,那么最好插入指示其他值的整數而不是存儲長字符串。

    但總的來說,是的向數據庫添加數據需要時間。

    這是一個有趣的讀物: http//dev.mysql.com/doc/refman/5.1/en/insert-speed.html

    暫無
    暫無

    聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

     
    粵ICP備18138465號  © 2020-2024 STACKOOM.COM