一、xml文件的主要构成
Xml代码
< File > < FileType >1</ FileType > < Num >1000000</ Num > <!-- 上面是文件头 下面是百万个<List> --> < List > < Msisdn >10350719507</ Msisdn > < State >1</ State > < state >45000</ state > </ List > ... <!-- 可能百万个 <List> 块--> </ File > |
二、先和大家共享一下我是处理xml文件生成的
1、要生成的数据小于 10000条的情况
在性能要求不高的情况下,你怎么操作,怎么搞都行,10000条数据基本上不会对java的内存造成多大的负担.
比较好用的方法是使用开源框架,比如XStream 直接把javabean 生成 xml
优点:api操作简单,方便维护
缺点:数据量大的情况下太消耗内存
2、大数据量生成一个xml文件(本程序采用的方法),数据条数 >= 10W
简单的说一下这种程序的开发思路
我们简单的把要生成的xml文件分为 3个部分,这部分需要找文件的规则,看一下哪一个部分是重复的,就把这部分单独提出出来.
1、文件头部分
2、重复循环部分(这部分对应数据库的内容)
3、文件尾部分
我是这么处理的:
自己做的一个可以使用极少的内存生成无限制大的xml文件框架由3部分生成xml文件
第一部分:生成文件头
例如: xxx.toXML(Object obj, String fileName)
第二部分:通过每次向文件里面追加10000(可配置)条数据的形式生成文件块
例如:xxx.appendXML(ArrayList);
第三部分:生成xml文件尾巴
例如:xxx.finishXML();
程序中的调用:调用xxx.toXML(Object obj, String fileName) 生成文件头之后, 可以循环从数据库中读取数据生成ArrayList,通过xxx.appendXML(Object object) 方法追加到xml文件里面,xxx.finishXML() 对文件进行收尾
上面就是大体的思路,有了思路之后,大家可以尝试着自己写一个类似的大数据处理框架
三、如何检测百万级别以上的处理程序的性能问题,如何优化
1、根据问题凭借自己的经验检测代码
1、检测一下程序中使用的List,Map 或者new的对象有没有及时的释放.
这一部分非常重要,及时的释放对象的资源并且手动置空对象可以快速的释放对象所在的内容。
我个人根据文件崩溃时候的日志发现是在生成xml的框架里面报的错误,第一想到的是框架有些资源没有释放.于是把自己做的文件生成框架整体的排查了一遍,并且自己写个简单程序生成200万条数据,使用xml框架生成一个xml文件,整个生成过程中任务管理器(xp)查看程序对应的java进程使用的内存基本在20M左右,因此排除框架的问题.怀疑是数据库查询和调用框架的部门出现问题.
如果这一块不能发现什么问题的话,就应该果断的放弃检测,使用内存检测工具
2、使用JProfile 内存检测工具进行分析
在JProfile上面多跑几次程序,运行50W数据后,通过检测中没词都发现 String[] 和 oracle.jdbc.driver.Binder[] 两种对象的数目在增加,基本上不会有大幅下降,而且数目基本上差不多,由于java.long.String[]对象是需要依赖对象而存在的,因此问题可能出在oracle.jdbc.driver.Binder[]上面,可能是 该对象没有释放导致引用导致String[]不能正常回收.
直接通过jprofile的对象引用关联检测到oracle.jdbc.driver.Binder 被 oracle驱动的 T4CPreparedStatement 类关联,而T4CPreparedStatement正好是Oracle对jdbc OraclePreparedStatement的具体实现,因此断定是在数据库处理方面出现的问题导致oracle.jdbc.driver.Binder对象不能正常释放,有了具体的目标之后马上又去专门排查数据库操作的代码,排查jdbc数据查询的问题,把问题的矛头直至数据库的批处理和事务处理.因为此程序是每生成一个文件成功后,会把已经处理的数据转移到对应的历史表中进行备份,而再个表操作的过程中使用了批处理和事务,使用批处理主要是保证执行速度,使用事务主要是保证同时成功和失败。
3、又因此程序每次从数据库中查询3000条数据处理,所以准备监控oracle.jdbc.driver.Binder的对象数目是否和查询次数对应.,通过在程序中Sysout输出查询次数 + JProfile运行GC测试 Binder,数据匹配,证实是java在数据库批处理的过程中有些问题.
4、把批处理代码提取出来通过JProfile内存分析.最后终于定位出问题的原因.
分析结果如下:100W以上数据生成文件的过程中,等文件生成之后才执行commit()操作提交事务, 100W数据按照3000一批写入文件, 每批次只是通过 PreparedStatement.addBatch();加入到批次里面去,并没有执行PreparedStatement.executeBatch(),而是在commit()之前统一调用的PreparedStatement.executeBatch(),这样的话PreparedStatement就会缓存100W条以上数据信息,造成了内存溢出.
错误的方法如下:
Java代码
try { conn.setAutoCommit( false ); pst = conn.prepareStatement(insertSql); pstDel = conn.prepareStatement(delSql); pstUpdate = conn.prepareStatement(sql); ... //totalSize = 100W数据 / 3000一批次 for ( int i = 1 ; i <= totalSize; i++) { client.appendXML(list); } // 错误的使用方法 client.finishXML(); pst.executeBatch(); pstDel.executeBatch(); } ... finally { try { if (isError) { conn.rollback(); } else conn.commit(); ... } ... } |
正确的方法如下
Java代码 |
try { conn.setAutoCommit( false ); pst = conn.prepareStatement(insertSql); pstDel = conn.prepareStatement(delSql); pstUpdate = conn.prepareStatement(sql); ... //totalSize = 100W数据 / 3000一批次 for ( int i = 1 ; i <= totalSize; i++) { list = 从数据库中查询 3000 条数据 client.appendXML(list); pst.executeBatch(); pstDel.executeBatch(); } client.finishXML(); ... inally { try { if (isError) { conn.rollback(); } else conn.commit(); ... } ... |
原创文章@ 转载请标明出处