Java 近百万数据查询和导出共用时2分钟左右

特别感谢 https://www.cnblogs.com/barrywxx/p/10700283.html 提供的模板导出的技术和思路

测试环境:

1、jdk1.8  springboot  mybatis  mysql

2、70万条的数据34个字段,因业务需求是60万左右的数据量我这里只测试了70万的

技术和思路:

查询:采用流式查询,每次查询5万条数据

导出:采用模板的形式。用Excel的xml格式处理

代码展示:

controller层:


import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.antlr.stringtemplate.StringTemplate;
import org.antlr.stringtemplate.StringTemplateGroup;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import cn.stylefeng.guns.modular.zcManageTemp.service.XMLExportService;

@Controller
@RequestMapping("/batchExport")
public class XMLExport {
	@Autowired
	private XMLExportService XMLExportService;

	@ResponseBody
	@RequestMapping("/exprot")
	public void getData() throws Exception {
		long startTimne = System.currentTimeMillis();
		//模板对象。stringTemplate:只是一个标识,可随便命名
		StringTemplateGroup stGroup = new StringTemplateGroup("stringTemplate");
		// 写入excel文件头部信息
		//这里获取的模板路径是在classpath下的。默认是 resources下的
		StringTemplate head = stGroup.getInstanceOf("template/head");
		//创建导出的文件,如果存在会覆盖
		File file = new File("D:/test/yunda/output11.xls");
		PrintWriter writer = new PrintWriter(new BufferedOutputStream(new FileOutputStream(file)));
		//对模板里的参数进行设置。我这里就直接设置了要导出的数据行数,列数在模板里写死了也可在这里设置。
		//如果数据行数以及列数小于你要导出的数据,则文件打不开。大于等于则无影响
		head.setAttribute("rows", "700100");
		writer.print(head.toString());
		writer.flush();
		// 写入excel文件数据信息
		StringTemplate body = stGroup.getInstanceOf("template/body");
		//分批次查询数据和导出数据.导出70万的数据,每次查询导出5万。如果服务器性能好,可以设置大点
		int size = 700000;
		int cs = 50000;
		if (size > 0) {
			int sang = size / cs;
			int yu = size % cs;
			for (int i = 0; i <= sang; i++) {
				List> selectZCTestData = null;
				if(i == 0) {
					selectZCTestData = new ArrayList>(50001);
					//第一行行头
					selectZCTestData.addAll(getMap());//注意:这里不要放到for循环外面,否者会出现每次循环都会有这一行的数据(具体原因没找到)
				}else {
					selectZCTestData = new ArrayList>(50000);
				}
				long l1 = System.currentTimeMillis();
				
				//数据查询分页:此处用到了分流查询。否则会出现内存溢出。
				//如果这里用了分流查询但是服务器的jvm内存太小会导致卡死的问题。我在本地电脑跑的给了1024*3的大小
				Map map = new HashMap();
				map.put("star", i * cs);
				map.put("page", cs);
				if (i == sang) {
					if (yu > 0) {
						selectZCTestData.addAll(XMLExportService.selectZCTestData(map));
					}
				} else {
					selectZCTestData.addAll(XMLExportService.selectZCTestData(map));
				}
				System.out.println(i * cs);
				long l2 = System.currentTimeMillis();
				System.out.println("第" + i + "次查询用时=" + (l2 - l1));
				//数据写出
				if (selectZCTestData.size() > 0) {
					Worksheet worksheet = new Worksheet();//这个对现场是自己创建的,用于模板里
					worksheet.setRows(selectZCTestData);
					body.setAttribute("worksheet", worksheet);
					writer.print(body.toString());
					writer.flush();
					selectZCTestData.clear();
					selectZCTestData = null;
				}

				long l3 = System.currentTimeMillis();
				System.out.println("第" + i + "次导出用时=" + (l3 - l2));
				//清空数据
				XMLExportService.clearList();
			}
		}
		body = null;
		// 写入excel文件尾部:跟模板要匹配
		writer.print("");
		writer.print("");
		writer.print("");
		writer.flush();
		writer.close();
		long endTime = System.currentTimeMillis();
		System.out.println("导出用时=" + ((endTime - startTimne) / 1000) + "秒");
	}
	private List> getMap() {
		List> retlist=new ArrayList>();
		Map map = new HashMap<>();
		List list = getList();
		list.forEach(p -> {
			map.put(p, p);
		});
		retlist.add(map);
		return retlist;
	}

	private List getList() {
		List list = new ArrayList();
		list.add("code_id");
		list.add("zichan_code");
		list.add("ciji_num");
		list.add("pdzc_name");
		list.add("gongsi_code");
		list.add("bumen_code");
		list.add("zichan_name");
		list.add("zichan_nature");
		list.add("zichan_fenlei");
		list.add("zichan_guige");
		list.add("zichan_type");
		list.add("zichan_brand");
		list.add("supplier");
		list.add("laiyuan_way");
		list.add("zichan_value");
		list.add("zichan_zhejiu");
		list.add("zichan_netvalue");
		list.add("zichan_num");
		list.add("zichan_durable");
		list.add("baoxiuqi");
		list.add("jilaing_unit");
		list.add("zhuzichan");
		list.add("caigou_code");
		list.add("ruku_riqi");
		list.add("ruzhang_riqi");
		list.add("shebei_code");
		list.add("zeren_man");
		list.add("zeren_bumen");
		list.add("zichan_suoshu");
		list.add("zichan_cunfang");
		list.add("shiyong_man");
		list.add("shiyong_bumen");
		list.add("zichan_state");
		list.add("remark");
		return list;
	}
}

service层:

private List> selectZCTestDataList = new ArrayList>(50000);
	@Override
	public List> selectZCTestData(Map map) {
		//流式读取
		GxidResultHandler gxidResultHandler = new GxidResultHandler();
		try {
			this.baseMapper.selectZCTestData(map, gxidResultHandler);
			selectZCTestDataList.addAll(gxidResultHandler.getData());
		} finally {
			gxidResultHandler.end();
		}
		return selectZCTestDataList;
	}

	public void clearList() {
		selectZCTestDataList.clear();
	}

流式获取结果类:

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;

public class GxidResultHandler implements ResultHandler> {
	// 存储每批数据的临时容器
	private List> list = new ArrayList>(50000);
	int size = 0;
	@Override
	public void handleResult(ResultContext> resultContext) {
		/** 回调处理逻辑 */
		list.add(resultContext.getResultObject());
	}
	
	public List> getData() {
		return list;
	}

	// 这个方法给外面调用,用来完成最后一批数据处理
	public void end() {
		list.clear();
	}
}

dao层:

void selectZCTestData(Map map,ResultHandler> handler);

worksheet:

package cn.stylefeng.guns.modular.zcManageTemp.controller;

import java.util.List;
import java.util.Map;

/**
 * 类功能描述:Excel sheet Bean
 *
 */
public class Worksheet {
    private String sheet;
    private int columnNum;
    private int rowNum;
    private List> rows;

    public String getSheet() {
        return sheet;
    }
    public void setSheet(String sheet) {
        this.sheet = sheet;
    }

    public List> getRows() {
        return rows;
    }
    public void setRows(List> rows) {
        this.rows = rows;
    }

    public int getColumnNum() {
        return columnNum;
    }
    public void setColumnNum(int columnNum) {
        this.columnNum = columnNum;
    }

    public int getRowNum() {
        return rowNum;
    }
    public void setRowNum(int rowNum) {
        this.rowNum = rowNum;
    }
}

文件导出的模板:

head.st




 
 
 
  
 
 
  8988
  23040
  32767
  32767
  False
  False
 
 
  
 
 
  

body.st

$worksheet:{
   $it.rows:{
   	
		$it.code_id$
		$it.zichan_code$
		$it.ciji_num$
		$it.pdzc_name$
		$it.gongsi_code$
		$it.bumen_code$
		$it.zichan_name$
		$it.zichan_nature$
		$it.zichan_fenlei$
		$it.zichan_guige$
		$it.zichan_type$
		$it.zichan_brand$
		$it.supplier$
		$it.laiyuan_way$
		$it.zichan_value$
		$it.zichan_zhejiu$
		$it.zichan_netvalue$
		$it.zichan_num$
		$it.zichan_durable$
		$it.baoxiuqi$
		$it.jilaing_unit$
		$it.zhuzichan$
		$it.caigou_code$
		$it.ruku_riqi$
		$it.ruzhang_riqi$
		$it.shebei_code$
		$it.zeren_man$
		$it.zeren_bumen$
		$it.zichan_suoshu$
		$it.zichan_cunfang$
		$it.shiyong_man$
		$it.shiyong_bumen$
		$it.zichan_state$
		$it.remark$
   
   }$
}$

 

结果展示:

0
第0次查询用时=2276
第0次导出用时=3575
50000
第1次查询用时=2090
第1次导出用时=4427
100000
第2次查询用时=2004
第2次导出用时=3705
150000
第3次查询用时=2229
第3次导出用时=3561
200000
第4次查询用时=2374
第4次导出用时=3502
250000
第5次查询用时=2816
第5次导出用时=3452
300000
第6次查询用时=3094
第6次导出用时=2871
350000
第7次查询用时=3085
第7次导出用时=3530
400000
第8次查询用时=2956
第8次导出用时=3148
450000
第9次查询用时=4073
第9次导出用时=3009
500000
第10次查询用时=4384
第10次导出用时=3163
550000
第11次查询用时=5824
第11次导出用时=3053
600000
第12次查询用时=4513
第12次导出用时=3689
650000
第13次查询用时=32630
第13次导出用时=3628
700000
第14次查询用时=0
第14次导出用时=0
导出用时=129秒

结束

从这次结果看,最后一次的查询耗时非常多,可能原因:1、jvm内存 2、主机性能  。总体来说勉强能用,但是还需要继续优化

你可能感兴趣的:(大量数据查询,大量数据导出)