一.背景说明
之前在处理一个优化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
原文地址:https://www.cnblogs.com/zhuqingxin/p/9866704.html