[英]how do I load 100 million rows in to memory
我需要從MySQL數據庫加載1億多行到內存中。 我的java程序失敗了java.lang.OutOfMemoryError: Java heap space
我的機器中有8GB RAM,我在JVM選項中給了-Xmx6144m。
這是我的代碼
public List<Record> loadTrainingDataSet() {
ArrayList<Record> records = new ArrayList<Record>();
try {
Statement s = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY);
s.executeQuery("SELECT movie_id,customer_id,rating FROM ratings");
ResultSet rs = s.getResultSet();
int count = 0;
while (rs.next()) {
知道如何克服這個問題嗎?
我發現了這篇文章 ,並根據下面的評論更新了我的代碼。 我似乎能夠以相同的-Xmx6144m數量將數據加載到內存中,但這需要很長時間。
這是我的代碼。
...
import org.apache.mahout.math.SparseMatrix;
...
@Override
public SparseMatrix loadTrainingDataSet() {
long t1 = System.currentTimeMillis();
SparseMatrix ratings = new SparseMatrix(NUM_ROWS,NUM_COLS);
int REC_START = 0;
int REC_END = 0;
try {
for (int i = 1; i <= 101; i++) {
long t11 = System.currentTimeMillis();
REC_END = 1000000 * i;
Statement s = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
java.sql.ResultSet.CONCUR_READ_ONLY);
s.setFetchSize(Integer.MIN_VALUE);
ResultSet rs = s.executeQuery("SELECT movie_id,customer_id,rating FROM ratings LIMIT " + REC_START + "," + REC_END);//100480507
while (rs.next()) {
int movieId = rs.getInt("movie_id");
int customerId = rs.getInt("customer_id");
byte rating = (byte) rs.getInt("rating");
ratings.set(customerId,movieId,rating);
}
long t22 = System.currentTimeMillis();
System.out.println("Round " + i + " completed " + (t22 - t11) / 1000 + " seconds");
rs.close();
s.close();
}
} catch (Exception e) {
System.err.println("Cannot connect to database server " + e);
} finally {
if (conn != null) {
try {
conn.close();
System.out.println("Database connection terminated");
} catch (Exception e) { /* ignore close errors */ }
}
}
long t2 = System.currentTimeMillis();
System.out.println(" Took " + (t2 - t1) / 1000 + " seconds");
return ratings;
}
要加載前100,000行,需要2秒鍾。 要加載29個100,000行,需要46秒。 我在中間停止了這個過程,因為它耗費了太多時間。 這些可接受的時間是多少? 有沒有辦法提高這段代碼的性能? 我在8GB RAM 64位Windows機器上運行它。
一億條記錄意味着每條記錄最多可占用50個字節,以便適合6 GB以內的額外空間用於其他分配。 在Java中,50字節不算什么; 僅僅Object[]
每個元素需要32個字節。 您必須找到一種方法來立即在while (rs.next())
循環中使用結果, while (rs.next())
不是完全保留它們。
問題是我在s.executeQuery中得到了java.lang.OutOfMemoryError(自行排序)
您可以將查詢拆分為多個:
s.executeQuery("SELECT movie_id,customer_id,rating FROM ratings LIMIT 0,300"); //shows the first 300 results
//process this first result
s.executeQuery("SELECT movie_id,customer_id,rating FROM ratings LIMIT 300,600");//shows 300 results starting from the 300th one
//process this second result
//etc
當找不到更多結果時,您可以暫停一段時間
你可以調用stmt.setFetchSize(50);
和conn.setAutoCommitMode(false);
避免將整個ResultSet讀入內存。
這是文檔所說的內容:
根據游標獲取結果
默認情況下,驅動程序立即收集查詢的所有結果。 這對於大型數據集來說可能不方便,因此JDBC驅動程序提供了一種將ResultSet基於數據庫游標並僅獲取少量行的方法。
在連接的客戶端緩存少量行,當用盡時,通過重新定位光標來檢索下一行行。
注意:
ResultSet.TYPE_FORWARD_ONLY
創建Statement。 這是默認值,因此不需要重寫代碼以利用此功能,但這也意味着您無法向后滾動或以其他方式在ResultSet中跳轉.- 示例 :設置提取size
以打開和關閉光標。
將代碼更改為游標模式就像將Statement的獲取大小設置為適當的大小一樣簡單。 將獲取大小設置為0將導致所有行被緩存(默認行為)。
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/test?useCursorFetch=true&user=root");
// make sure autocommit is off
conn.setAutoCommit(false);
Statement st = conn.createStatement();
// Turn use of the cursor on.
st.setFetchSize(50);
ResultSet rs = st.executeQuery("SELECT * FROM mytable");
while (rs.next()) {
System.out.print("a row was returned.");
}
rs.close();
// Turn the cursor off.
st.setFetchSize(0);
rs = st.executeQuery("SELECT * FROM mytable");
while (rs.next()) {
System.out.print("many rows were returned.");
}
rs.close();
// Close the statement.
st.close();
您必須重新設計並以塊的形式將數據加載到內存中。
例
1)使用適當的SQL(sql僅選擇100萬)從數據庫加載前100萬條記錄,然后處理2)加載另一個類似的塊。
單獨的setFetchSize不會解決這個問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.