Springboot+Mysql流式查询——避免内存溢出(OOM)

话说笔者最近在做一个数据迁移的功能,说白了就是数据导出导入嘛。寻思着这还不简单,没想到第一步就GG了。

具体就是在数据库查询返回数据集时,由于数据集过大(大约几十万条吧)导致出现了内存溢出,即后台报错OOM。面对这种问题我们的第一反应肯定是将结果分页返回。但是分页却不得不考虑到几个问题:首先就是每页的大小,需要经过测试来确定一个合适的值,如果页面过大仍然可能出现内存溢出的问题;其次就是对于结果超大的数据集,在分页时偏移性能、查询开销和结果预加载似乎真的会损害分页方法的效率,简而言之就是会比较慢。

经过一翻查找,总算找到了比较适合的方法,即流式查询。这种查询是在java8进行支持的,所以首先需要我们的jdk在8以上。

在这里插入图片描述

先说使用效果吧,将50多万条数据查询并写入csv文件,以及下载,所需时间不足1分钟。

那么,它是如何实现的呢。很简单。

在使用 MySQL 时,为了真正流式传输结果,我们需要满足三个条件:
①设置结果集游标只能向前(Spring默认了)
②只读(设置@Transactional(readOnly = true)即可)
③提取大小设置为 Integer.MIN_VALUE(解释一下,这代表逐行读取)

具体步骤使用如下:
第一步,先在repository中写sql查询数据,返回类型为Stream

    @QueryHints(value = @QueryHint(name = HINT_FETCH_SIZE, value = "" + Integer.MIN_VALUE))
    @Query(value = "select t from device t")
    Stream<Device> streamAll();

第二步:service方法中加入@Transactional(readOnly = true),并调用第一步查询方法

    @Autowired
    private EntityManager entityManager;

    @GetMapping(value = "/csv")
    @Transactional(readOnly = true)
    public void exportTodosCSV(HttpServletResponse response) {
        response.addHeader("Content-Type", "application/csv");
        response.addHeader("Content-Disposition", "attachment; filename=todos.csv");
        response.setCharacterEncoding("UTF-8");
          try(Stream<Device> todoStream = deviceRepository.streamAll()) {
           PrintWriter out = response.getWriter();
           todoStream.forEach((Device device) -> {
                String deviceString = JSON.toJSONString(device);
                out.write(deviceString);
                out.write("\n");
                entityManager.detach(device);
            });
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

第三步 :上述代码中的entityManager.detach(device) 负责删除缓存的对象,只有这样才能保证内存即用即销。另外EntityManager 需要注入。
在这里插入图片描述

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值