1. 程式人生 > >JDBC處理大結果集

JDBC處理大結果集

索引 網絡 sdn 展示 eve 限制 order rowno ati

一.背景說明

之前在處理一個優化task時接觸到了Jdbc對於大結果集的處理相關內容,記錄下。問題開始於前置機抽數程序在抽取大數據量數據時出現卡死的情況。數據源方面客戶使用的是Oracle數據庫,使用Jdbc直接連接數據庫。根據字段數量及類型不同,大概在單個接口數據量達到10萬~15萬左右就會出現卡死。

二.原因探尋

猜測一:
一開始以為是因為寫入的csv文件對大小及記錄數有限制,因為每次大概都是寫到70多M的時候就會卡死,而且比如xls文件的根據office版本不同確實是有限制的。但經過網上搜索及實際驗證後發現,並不是因為這個原因。不過這個地方還是可以找到可以優化的點,就是按大小或記錄數分批寫文件,當行數真的有要求時可以類似於這樣做:

while ( rs.next() ) /* 此處處理業務邏輯 */
{
count++;
if ( count % pageSize == 0 )
{
System.out.println( " 寫入到第 " + (count/pageSize) + " 個文件中!" );

}

}

猜測二:
這個時候又在想應該一次性處理的結果集太大導致無法處理,哪我們能不能試試把結果集分割下,或者使一次處理的結果集盡量小點呢?由此,研究了下分頁查詢的用法,Oracle裏暫時還不能用limit,所以是借助偽列rownum來實現的,如下:
"select * from (select rownum as rowno,ai.* " +

"from ai, " +
" mv, " +
"hscf.hscf_approved_ou_vendors haov, "+
"hscf.hscf_apr_ou_vdr_sync_timestamp haov_ts "+
"where ai.view_id = mv.mv_id " +
" " +
"and ai.org_id = haov.operating_unit_id " +
"and mv.vendor_id = haov.vendor_id " +
"and ? = haov.interface_entity_code " +
"and ‘Y‘ = haov.enabled_flag " +
"and haov.interface_entity_code = haov_ts.interface_entity_code " +
"and haov.operating_unit_id = haov_ts.operating_unit_id " +
"and haov.vendor_id = haov_ts.vendor_id "+
"and rownum <= * ) " +
"where rowno > ( -1) * " ;

但是這個用上之後,發現其實並不能解決問題。甚至就總的效率而言,反而是下降了。因為每次找到分頁的起始點其實也是會花費時間的,頁數越往後時間花的越多。由此可以記錄一下:分頁並不適用於提高總的效率。只是有些時候我們不需要獲取所有記錄,只是想查看一部分的時候,比如前端的頁面展示。實際上,因為我們這裏為了提高查詢效率使用了物化視圖,該建的索引都建了,去掉了不必要的Order by,還使用了PARALLEL特性。所以到這裏可以發現問題的關鍵並不在於磁盤IO,SQL效率上,應該從別的角度去考慮。

猜測三:
上面的方法雖然並沒有實際解決問題,但其實還是提供了一些有用的想法,且讓我有所收獲的。到這裏其實我們應該冷靜下來,好好去理一下思路,可以知道重點其實應該在結果集上,所以我們應該好好的研究下ResultSet這個類的各個屬性方法。
由此,我獲得了一些有用的信息,並最終結局了問題。Jdbc只是接口規範,實際的實現還在於驅動類,說這句話的目的在於提醒,解決這種問題一定要具體到數據庫類型。比如,網上說使用setFetchSize修改每次提取的記錄數可以解決Jdbc大數據量抽取效率的問題,但我看了下人家用的是Mysql。而在我的這個案例裏,數據庫用的Oracle,每次默認的fetchSize為10,而這個其實是有Oracle給出的一個比較合理的大小。因為,一次發送的太多,每次花費在網絡傳輸上的時間也會變多。經過反復查找,終於發現問題的關鍵在於這裏:
//modified by zhuqingxin at 2018-09-05 for large data extract
//pst = getConnection().prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
pst = getConnection().prepareStatement( sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY );
//modified by zhuqingxin at 2018-09-05 for large data extract
原來的那種結果集設置不合理,導致JVM會把結果集主要存儲於內存中,而系統分配給JVM的內存應該是有限制的。當數據量很大的時候,自然就會出問題。後面那種設置按我理解,應該就是主要把結果集存儲於磁盤,到需要的時候再取了。這個優化修改,總共200多M的數據文件大概15分鐘全部處理完成並發送到服務器。所以,我之前做的那些分批寫文件,後臺分頁查詢,把文件改成文件列表之類的其實都沒什麽用。解決問題的關鍵其實就一句話。

三.參考

https://www.cnblogs.com/jay763190097/p/6813792.html
https://www.cnblogs.com/anee/archive/2011/12/20/2675796.html
https://blog.csdn.net/IT_xiaocao/article/details/64920381?locationNum=2&fps=1
https://blog.csdn.net/seven_3306/article/details/9303979
https://blog.csdn.net/seven_3306/article/details/9303879
https://www.cnblogs.com/bukudekong/archive/2011/06/22/2086528.html
https://blog.csdn.net/ooad/article/details/3317705
https://wenku.baidu.com/view/72207982fab069dc502201c3.html
https://www.cnblogs.com/zhangzhxb/p/6336484.htm

JDBC處理大結果集