背景
這邊我們做了一個產品的小功能,把mysql的數據拉取到hdfs上來
代碼實現
代碼很簡單,就是簡單的JDBC
long start = System.currentTimeMillis();
PreparedStatement preparedStatement = connection.prepareStatement("select * from ceshi");
ResultSet resultSet = preparedStatement.executeQuery();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
long end = System.currentTimeMillis();
System.out.println("------"+Thread.currentThread()+"獲取數據消耗時間"+(end-start)/1000);
讀取數據,然後hdfs的IO流寫入到hdfs上面。但是我發現在讀取數據的時候會出現卡頓的情況。(我這邊數據量大約100W),我打印了日誌,發現在接收數據的時候,耗時大約在10秒,
後來開始查資料發現mysql查詢的機制是jdbc默認的讀取數據的時候,會將要查詢的數據一次性讀到內存中,再通過resultSet循環讀取出來。
調研了下發現可以通過JDBC 流的方式來讀取數據,這種方式可以將讀取出來的每一條數據查詢到客戶端,再執行insert操作,這樣機器負載就會輕很多。
相關源碼如下
/**
* We only stream result sets when they are forward-only, read-only, and the
* fetch size has been set to Integer.MIN_VALUE
*
* @return true if this result set should be streamed row at-a-time, rather
* than read all at once.
*/
protected boolean createStreamingResultSet() {
return ((this.query.getResultType() == Type.FORWARD_ONLY) && (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY)
&& (this.query.getResultFetchSize() == Integer.MIN_VALUE));
}
然後之前的代碼如下
long start = System.currentTimeMillis();
PreparedStatement preparedStatement = connection.prepareStatement("select * from ceshi",ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
preparedStatement.setFetchSize(Integer.MIN_VALUE);
ResultSet resultSet = preparedStatement.executeQuery();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
long end = System.currentTimeMillis();
System.out.println("------"+Thread.currentThread()+"獲取數據消耗時間"+(end-start)/1000);
然後就會發現獲取數據不會消耗時間,大大提升了效率。
順便說一下 不添加 這兩個參數ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY 也可以,因爲mysql connection.prepareStatement(sql)中就默認設置這兩個參數的。感興趣的可以直接看下源碼。
所以爲了方便也可以直接設置
preparedStatement.setFetchSize(Integer.MIN_VALUE);
參考資料:https://blog.csdn.net/zhouxukun123/article/details/83476715