最近遇到一个问题,从一张6000w条数据的表(这张表已经做了分区,这是一个月的数据)中查询数据(项目用了hibernate),然后导出成Excel文件,已知查询最多的导出是1700w条,这个功能之前就做好了,做法是分批查询(一次30000)出数据后在本地生成Excel文件,查询完毕后再把这些文件打包成压缩文件返回到客户端,这个过程对于数据量很少的查询还勉强说得过去,但是对于少数用户数据量很大,1700w用了4小时,这是响应时间,页面早已经超时了。
反馈这个bug后让我改进,我的做法先是将查询改成多线程操作,一次多个分批查询然后把在本地生成临时文件的过程去掉,直接利用压缩流返回给客户端,这样是解决了响应超时的问题(响应时间还是很长,因为第一步会有个select count(*)的操作,这句sql执行很慢),但是导出还是很慢,因为分批查询是利用oracle的分页查询,越往后查询越慢,有的线程会遇到数据库连接(连接池)超时的问题,而导致数据不完整会缺少文件。
我只是一个刚毕业有3个月开发经验的程序员,就遇到这么棘手的问题,个人感觉这个功能需求提的就有点不太现实,想问有这方面经验的大神,这个问题有什么好的解决方法?
大数据?导出?高性能?oracle
------解决方案--------------------
对于数据规模太大的,做成任务。
用户点击导出,检查他的数据规模,超过50W的,提示其数据规模过大,可能需要 N 小时完成,请稍候再来查询和下载导出结果。如果用户点击确定,你就提示:“任务已进入队列,点击此连接查询导出进度。”
然后你要做两件事情:
1、给这个用户记录个标识,就是他已经启动某导出任务,不能再启动新的了(或者限制一个人最多同时启动几个导出任务);
2、后台有个调度程序,开始执行导出工作,并将生成的Excel放在某磁盘目录或存在数据库中;这个调度任务可以控制下最大同时并发的导出任务数,以避免任务太多拖垮系统。
另外需要开发界面查询导出进度以及下载导出结果。导出结果可以考虑一个最大保存周期,比如7天。
------解决方案--------------------
无。原因很简单,Oracle查询时,要根据你的条件对数据进行过滤和排序,假定你要取出 100W~101W 的数据,那么在没有完成对所有数据的过滤和所有数据的排序(注意不仅仅是100W,因为有排序)之前,Oracle无法知道100W~101W究竟会是哪些。
所以,除了7楼的办法外(7楼的办法涉及到更新字段和更新索引,其实性能也有限),另一种办法就是定位检索。也就是不做任何过滤与排序。
假如你所有的记录都有一个不间断顺序号,从1~100W,那么你可以直接让Oracle给你返回:
Select * From 表 Where id > 99W And id <=100W
这个就是定位检索,Oracle直接根据索引找到id为99W的位置,然后顺序返回后面1W行,超高效率。
如果你的分页查找过程是连续的,也可以延伸该做法使其适应于非连续主键,记住上次一检索的最后一个ID大小即可:
Select * From (Select *, rownum _rn_ From 表 Where id>上一次ID) Where _rn_ <= 10000
整个过程都时刻要关注两个问题:1、过滤(只能适应于Range型);2、排序(Never Do That)。
------解决方案--------------------
如果我没理解错的话,第一个方法在实际环境不太适用,一般查询条件是不会用到主键的。第二个方法是个好主意,大神果然经验丰富。
第一次查询:select * from 表 where 需要的查询条件 and rownum < 100W order by 主键
以后的查询条件:select * from 表 where 需要的查询条件 and id > 上次的最后一个id and rownum < 100W order by id