![](/img/trans.png)
[英]OutOfMemoryError while inserting data from large JSON file into MySQL database
[英]MySQL Inserting large data sets from file with Java
我需要從CSV文件中將大約180萬行插入到MySQL數據庫中。 (只有一張桌子)
目前使用Java來解析文件並插入每一行。
你可以想象這需要花費幾個小時才能運行。 (10)
我之所以沒有將它直接從文件傳輸到數據庫中,是因為數據必須在將數據添加到數據庫之前進行操作。
這個過程需要由那里的IT經理來運行。 所以我把它設置為一個很好的批處理文件,讓它們在將新的csv文件放到正確的位置后運行。 所以,我需要通過將文件放到某個位置並運行批處理文件來很好地完成這項工作。 (Windows環境)
我的問題是,插入這么多數據的最快方法是什么? 大型插入,來自臨時解析文件或一次插入一次? 可能還有其他想法嗎?
第二個問題是,如何優化我的MySQL安裝以允許非常快速的插入。 (還有一點需要大量選擇所有數據)
注意:該表最終將被刪除,整個過程將在以后再次運行。
一些澄清:目前使用... opencsv.CSVReader解析文件,然后在每一行上插入。 我正在總結一些專欄而忽略其他專欄。
更多說明:本地DB MyISAM表
快速插入提示:
使用此插入語法:
插入表(col1,col2)值(val1,val2),(val3,val4),...
在插入之前刪除所有鍵/索引。
我可能會選擇一個很大的數字,比如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而是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
如果你使用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.