一直很糾結,Oracle的快速返回機制,雖然結果集不少,但是它能很快的顯示第一個結果,雖然經過MYSQl的客戶端能夠作到,可是經過JDBC卻不行。java
今天用了1個多小時,終於搞定此問題,但願對廣大Java朋友在處理數據庫時有個參考。mysql
來由:程序員
經過命令行客戶端加上-q參數,能夠極快的響應一個查詢。
好比結果集爲幾千萬的select * from t1,完整結果集須要20秒,經過-q參數顯示第一行只須要不到1秒。
但經過jdbc進行查詢,卻不能夠實現如上的效果,不管怎麼調整URL參數,也不行。
過程:
查看了-q參數的解釋,以下:
If you have problems due to insufficient memory for large result sets,
use the --quick option. This forces mysql to retrieve results
from the server a row at a time rather than retrieving the entire result set
and buffering it in memory before displaying it. This is done by returning
the result set using the mysql_use_result() C API function in the client/server
library rather than mysql_store_result().
可見,實現快速響應。
查看 mysql_use_result() 函數,這個是C的API,若是經過C開發,能夠用這個函數。
那麼JAVA呢?
查找標準JDBC規範裏面有關函數,沒有任何收穫。 setFetchSize()看上去有效,可在實際測試裏,無任何性能提高。
搜索 JDBC mysql_use_result, 有了意外的收穫。
在MYSQL的JDBC,com.mysql.jdbc.Statement 這個接口裏發現了以下的內容:
abstract public void disableStreamingResults() throws SQLException
Resets this statements fetch size and result set type to the values they
had before enableStreamingResults() was called.
abstract public void enableStreamingResults() throws SQLException
Workaround for containers that 'check' for sane values of Statement.setFetchSize()
so that applications can use the Java variant of libmysql's mysql_use_result() behavior.
原來MySQL提供了本身的一個快速響應的實現。調整測試代碼
stmt = (com.mysql.jdbc.Statement) con.createStatement();
stmt.setFetchSize(1);
//按行讀取
// 打開流方式返回機制
stmt.enableStreamingResults();
我期待的效果出現了。第一行數據被快速的現實出來,時間不到1秒中。
結論:
MySQL在本身的JDBC驅動裏提供了特有的功能,來實現查詢的快速響應,sql
特別是結果集很是大或者時間較長,而用戶很是想盡快看到第一條結果時特別有效。數據庫
from:http://blog.csdn.net/java2000_net/article/details/6869752性能優化
正確使用MySQL JDBC setFetchSize()方法解決JDBC處理大結果集 java.lang.OutOfMemoryError: Java heap space服務器
昨天在項目中須要對日誌的查詢結果進行導出功能。oracle
日誌導出功能的實現是這樣的,輸入查詢條件,而後對查詢結果進行導出。因爲日誌數據量比較大。多的時候,有上億條記錄。app
以前的解決方案都是屢次查詢,而後使用limit 限制每次查詢的條數。而後導出。這樣的結果是效率比較低效。函數
那麼能不能一次查詢就把全部結果倒出來了?因而我就使用一次查詢,不使用limit分頁。結果出現 java.lang.OutOfMemoryError: Java heap space問題。
看來是DB服務器端將一次將查詢到的結果集所有發送到Java端保存在內存中。因爲結果集比較大,因此出現OOM問題。
首先我想到的是遊標功能。那麼是否是可使用遊標,一次從服務器端慢慢的取呢?上網查詢了一下,你們都說MySQL不支持遊標功能等等。
後來就去看JDBC代碼。找到了setFetchSize()方法,結果設置之後,卻不能生效,仍是出現OOM問題。
個人設置以下
後來老大在MySQL看到了這樣的方法:
ps = (PreparedStatement) con.prepareStatement("select * from bigTable",
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
ps.setFetchSize(Integer.MIN_VALUE);
ps.setFetchDirection(ResultSet.FETCH_REVERSE);
對此解釋是:MySQL JDBC默認客戶端數據接收方式爲以下:
默認爲從服務器一次取出全部數據放在客戶端內存中,fetch size參數不起做用,當一條SQL返回數據量較大時可能會出現JVM OOM。
要一條SQL從服務器讀取大量數據,不發生JVM OOM,能夠採用如下方法之一:
一、當statement設置如下屬性時,採用的是流數據接收方式,每次只從服務器接收部份數據,直到全部數據處理完畢,不會發生JVM OOM。
setResultSetType(ResultSet.TYPE_FORWARD_ONLY);
setFetchSize(Integer.MIN_VALUE);
二、調用statement的enableStreamingResults方法,實際上enableStreamingResults方法內部封裝的就是第1種方式。
三、設置鏈接屬性useCursorFetch=true (5.0版驅動開始支持),statement以TYPE_FORWARD_ONLY打開,再設置fetch size參數,表示採用服務器端遊標,每次從服務器取fetch_size條數據。
設置之後,果真能夠解決個人問題。
附上代碼:
[java] view plaincopy
最近對JDBC有了進一步的瞭解。關於JDBC,推薦個人另外一篇文章,用於解決不寫文件,從Java IO流中直接導入數據到MySQL:
Java不寫文件,LOAD DATA LOCAL INFILE大批量導入數據到MySQL的實現 http://blog.csdn.net/chenyechao/article/details/9237495
推薦另外兩篇來自阿里巴巴葉正盛的文章我轉載的:
http://blog.csdn.net/chenyechao/article/details/9303979
這篇文章是我解決問題之後纔看到的,上面已經說明了MySQL JDBC的setFetchSize的使用。
另一篇:面向程序員的數據庫訪問性能優化法則 http://blog.csdn.net/yzsind/article/details/6059209