JDBC mysql大數據量流式讀取

總結下這周幫助客戶解決報表生成操作的mysql 驅動的使用上的一些問題,與解決方案。由於生成報表邏輯要從數據庫讀取大量數據並在內存中加工處理後再生成大量的彙總數據然後寫入到數據庫。基本流程是 讀取->處理->寫入。

讀取操作開始遇到的問題是當sql查詢數據量比較大時候基本讀不出來。開始以爲是server端處理太慢。但是在控制檯是可以立即返回數據的。於是在應用這邊抓包,發現也是發送sql後立即有數據返回。但是執行ResultSet的next方法確實阻塞的。查文檔翻代碼原來mysql驅動默認的行爲是需要把整個結果全部讀取到內存中才開始允許應用讀取結果。顯然與期望的行爲不一致,期望的行爲是流的方式讀取,當結果從myql服務端返回後立即還是讀取處理。這樣應用就不需要大量內存來存儲這個結果集。正確的流式讀取方式代碼示例:

PreparedStatement ps = connection.prepareStatement("select .. from ..", 
            ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); 


//forward only read only也是mysql 驅動的默認值,所以不指定也是可以的 比如: PreparedStatement ps = connection.prepareStatement("select .. from .."); 


ps.setFetchSize(Integer.MIN_VALUE); //也可以修改jdbc url通過defaultFetchSize參數來設置,這樣默認所以的返回結果都是通過流方式讀取.
ResultSet rs = ps.executeQuery(); 

while (rs.next()) { 
  System.out.println(rs.getString("fieldName")); 
}
代碼分析:下面是mysql判斷是否開啓流式讀取結果的方法,有三個條件forward-only,read-only,fatch size是Integer.MIN_VALUE
/**
 * 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() {
    try {
        synchronized(checkClosed().getConnectionMutex()) {
            return ((this.resultSetType == java.sql.ResultSet.TYPE_FORWARD_ONLY)
                 && (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY) && (this.fetchSize == Integer.MIN_VALUE));
        }
    } catch (SQLException e) {
        // we can't break the interface, having this be no-op in case of error is ok

        return false;
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章