記一個 MySQL Streaming result set 時的小錯誤

同事使用kettle遷移MySQL數據時出現了 SQLException,詳細報錯信息以下:html

Error reading from database: java.sql.SQLException: Streaming result set com.mysql.jdbc.RowDataDynamic@xxxxxxx is still active. No statements may be issued when any streaming result sets are open and in use on a given connection. Ensure that you have called .close() on any active streaming result sets before attempting more queries.

經查詢MySQL官方文檔(MySQL版本 5.1-8.0 同樣),發現報錯緣由是JDBC沒有處理完resultSet結果集時又使用同一個connection提交了新的query。下面是官方文檔對ResultSet的JDBC實現說明:java

默認狀況下,ResultSet會一次性返回結果集並保存在內存中。這也是最有效率且最容易實現的一種方式。可是當ResultSet含有大量數據(不少行、或者包含大對象的狀況下)時,JVM可能沒法分配ResultSet要求的內存,這時你可讓driver每次返回(stream)一行數據。mysql

要開啓這個功能,須要以以下的方式來建立Statement:sql

stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
              java.sql.ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(Integer.MIN_VALUE);

注意上面的statement建立語句,使用了 TYPE_FORWARD_ONLY、CONCUR_READ_ONLY,並將fetchSize設置爲 Integer.MIN_VALUE。這種設置告訴driver須要逐行處理 result set。數據庫

這種方式也有風險。在使用同一個 connection 來發起新的查詢以前,你必須:1)讀取 result set中的所有數據;2)或者將其關閉。不然就會拋出咱們前面所說的異常。併發

下面來講說事務。此 Statement 持有的鎖最快也要在statement 結束時才能釋放。這裏的鎖包括了 MyISAM的表級鎖或者InnoDB的行鎖。fetch

當 Statement處在事務中時,鎖將在事務完成時釋放(事務結束了,固然此Statement也完成了)。像大多數其餘數據庫同樣,Statement只能在其中的全部results都被讀取,或者相應的Result set被關閉以後才能關閉。spa

因此若是使用了streaming results 而又想併發訪問相應的表時,你就必須儘快來處理 statement 產生的result set。code

MySQL提供了一個可選的方案,即基於遊標的 streaming。與逐行掃描不一樣的是它能夠一次性讀取多行數據。要使用這種方式,你須要將 useCursorFetch 設置爲true,而後調用 setFetchSize 方法來設置你但願一次獲取的行數:htm

conn = DriverManager.getConnection("jdbc:mysql://localhost/?useCursorFetch=true", "user", "s3cr3t");
stmt = conn.createStatement();
stmt.setFetchSize(100);
rs = stmt.executeQuery("SELECT * FROM your_table_here");

emmm~,感受沒啥好補充的了。若是內存夠大就不必這麼麻煩,直接默認就行了。

相關文章
相關標籤/搜索