小工具      在线工具  汉语词典  css  js  c++  java

mybatis查询大批量数据

mybatis 额外说明

收录于:40天前

问题背景

公司中有很多场景需要运行批量数据。数据量从几十万到几千万不等。目前我们使用的是分页查询,但是分页查询有一个很深的分页问题。数以百万计的数据将很难查询。慢的

常规解决方案
  1. 完整查询
  2. 分页查询
  3. 流式查询
  4. 游标查询
1. 全量查询

默认情况下,全量查询的话系统会把所有结果集存储在内存中,在数据库中准备了大概200w的数据:

<select id="listUser" resultType="com.sun.ddd.infra.po.User">
        select * from user
</select>
@Test
public void test() {
    
    StopWatch stopWatch = new StopWatch();
    stopWatch.start("全量查询");
    List<User> users = userService.listUser();
    stopWatch.stop();
    System.out.println(stopWatch.getLastTaskName() + ":" + stopWatch.getLastTaskTimeMillis() + ":代码行数:" + users.size());
}
全量查询:21757:代码行数:2778523

利用JDK自带的java VisualVM监控全量查询时的内存占用情况

在这里插入图片描述

  • 可以很明显的看出200w的数据一次性查询占用总体内存1500MB,这个内存占用还是很大的,如果还有其他服务在运行,很容易导致OOM
2. 分页查询

为了解决全量查询占用内存过大,可能导致OOM问题,我们可以选择使用分页查询,这样就不会导致内存溢出问题了

@Override
public List<User> pageUser(Integer pageNum, Integer pageSize) {
    
    pageNum = (pageNum - 1) * pageSize;
	return userDao.pageUser(pageNum, pageSize);
}
<select id="pageUser" resultType="com.sun.ddd.infra.po.User">
    select * from user limit #{pageNum},#{pageSize}
</select>
@Test
public void test() {
    
    StopWatch stopWatch = new StopWatch();
    stopWatch.start("分页查询");
        int pageCount = 0;
        for (int i = 1; i < 1000; i++) {
    
            List<User> users1 = userService.pageUser(i, 2000);
            pageCount = pageCount + users1.size();
        }
        stopWatch.stop();
        System.out.println(stopWatch.getLastTaskName() + ":" + stopWatch.getLastTaskTimeMillis() + ":代码行数:" + pageCount);
    }
分页查询:285343:代码行数:1998000

在这里插入图片描述

  • 使用分页后,查询内存使用情况,最多占用内存不到500MB,是全量查询占用内存的1/3不到,但是由于深度分页和多次与数据库连接的缘故,导致整个查询时间很长,长达280s,如果数据更多点查询时间则更多
3. 流式查询

有没有什么方法既可以快速查看又占用内存少呢?答案当然是肯定的

客户端 JDBC 发起 SQL 查询,等待服务端准备数据。MySQL 服务端会向 JDBC 代表的客户端内核源源不断的输送数据,直到客户端请求 Socket 缓冲区满,这时的 MySQL服务端会阻塞。对于 JDBC 客户端而言,数据每次读取都是从本机器的内核缓冲区,所以性能会更快一些。类似服务端向客户端不断**push**的过程

标记是否使用流式传输:

 /** * We only stream result sets when they are forward-only, read-only, and the * fetch size has been set to Integer.MIN_VALUE * * @return true if this result set should be streamed row at-a-time, rather * than read all at once. */
    protected boolean createStreamingResultSet() {
    
        return ((this.query.getResultType() == Type.FORWARD_ONLY) && (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY)
                && (this.query.getResultFetchSize() == Integer.MIN_VALUE));
    }

其中我们只要关注this.query.getResultFetchSize() == Integer.MIN_VALUE,对应xml配置就是fetchSize="-2147483648"

<select id="listUserByStream" fetchSize="-2147483648" resultType="com.sun.ddd.infra.po.User">
        select * from user
</select>

这里mapper接口不需要返回值,因为数据都存储在ResultHandler<User>中了

void listUserByStream(ResultHandler<User> handler);
@Test
public void test() {
    
   StopWatch stopWatch = new StopWatch();
   stopWatch.start("流式查询");
        AtomicInteger totalCount = new AtomicInteger(0);
        userService.listUserByStream(context -> {
    
            // 处理查询结果
            context.getResultObject();
            totalCount.incrementAndGet();
        });
        stopWatch.stop();
        System.out.println(stopWatch.getLastTaskName() + ":" + stopWatch.getLastTaskTimeMillis() + ":代码行数:" + totalCount.get());
    }
流式查询:9967:代码行数:2778523

在这里插入图片描述

  • 同样是200w数据,可以明显看出查询时间只要9s多,占用内存也保持在500MB之内

    4. 游标查询

    客户端 JDBC 发起 SQL 查询,等待服务端准备数据。服务端数据准备完成后,进行数据传输,它允许应用程序在数据库服务器上打开一个游标并按需检索数据,而不是一次性获取整个结果集,类似客户端向服务端分批pull的过程。

    mapper接口层接收参数方式使用Cursor<User>

    Cursor<User> listUserByCursor();
    
    <select id="listUserByCursor" fetchSize="-2147483648" resultType="com.sun.ddd.infra.po.User">
        select * from user
    </select>
    
    @Test
    @Transactional
    public void test() {
          
       StopWatch stopWatch = new StopWatch();
     stopWatch.start("游标查询");
            AtomicInteger totalCountCursor = new AtomicInteger(0);
            Cursor<User> users2 = userService.listUserByCursor();
            for (User user : users2) {
          
                totalCountCursor.incrementAndGet();
            }
            stopWatch.stop();
            System.out.println(stopWatch.getLastTaskName() + ":" + stopWatch.getLastTaskTimeMillis() + ":代码行数:" + totalCountCursor.get());
        }
    

    由于 Cursor 是一个查询,因此会话将关闭。您需要将@Transactional 添加到方法中。

    游标查询:9813:代码行数:2778523
    

    在这里插入图片描述

  • 从测试结果来看,查询200w条数据时间跟流式查询差不多,占用的内存也不到500MB

总结:

查询方式 数据项数量 查询时间 使用的内存
完整查询 2778523 21757 1600MB
分页查询 1998000 285343 500MB
流式查询 2778523 9967 450MB
游标查询 2778523 9813 550MB

推荐使用流式查询,游标查询还跟指定数据库有关


我是1024,一个专注Java技术、记录生活的博主。

欢迎扫描二维码关注“1024公众号”,一起学习,一起进步,看更多路,少走弯路。

. . .

相关推荐

额外说明

8 款浏览器兼容性测试工具介绍,需要的赶紧收藏吧!

浏览器的兼容性问题,是指不同浏览器使用内核及所支持的 HTML 等网页语言标准不同,用户客户端的环境不同造成的显示效果不能达到理想效果。对于用户而言,无论使用哪款浏览器,期望看到的效果是正常的统一的。 市面上发布的浏览器版本非常之多,碍于测试环境和人力资

额外说明

内网穿透常见方式推荐

文章目录 1.可以使用nginx 1.1首先用ssh打开公网服务器和内网服务器的连接通道 1.2 nginx转发服务 2.中微子内网穿透 2.1 服务端docker部署 指定自己的mysql数据库 2.2服务端使用jar包自行部署 2.3 客户端jar部

额外说明

MYSQLg高级------聚簇索引和非聚簇索引

聚簇索引和非聚簇索引 innoDB MyISAM 扩展:(根据自己需求自行查看) 开始我们需要先了解点相关的知识,帮助大家更好的理解:(有基础的可以忽视,请大家多多包含) MySQL支持两种存储引擎分别是innoDB和MyISAM,默认使用innoDB存

额外说明

【OpenCV】⚠️高手勿入! 半小时学会基本操作 20⚠️ 直方图

【OpenCV】⚠️高手勿入!⚠️ 半小时学会基本操作 20⚠️ 概述 直方图 直方图 + mask 直方图均衡化 概述 OpenCV 是一个跨平台的计算机视觉库, 支持多语言, 功能强大. 今天小白就带大家一起携手走进 OpenCV 的世界. (第 2

额外说明

【Unity3D插件】DoTween插件研究

推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 我的个人博客 QQ群:1040082875 一、前言 DOTween是一个用于Unity的快速、高效、完全类型安全的面向对象动画引擎,为c#用户进行了优化,是免费和开源的,具

额外说明

course2610_lab7- linux shell 实现模拟多进程并发执行

1. shell 概念 1.1 shell shell(壳, 一种用C 语言编写的程序)是一个命令行解释器,是linux内核的一个外壳, 负责外界与linux内核的交互。 shell 是连接 用户与 Unix/Linux内核的桥梁,它通过调用系统核心的大

额外说明

FasterViT实战:使用FasterViT实现图像分类任务(二)

文章目录 训练部分 导入项目使用的库 设置随机因子 设置全局参数 图像预处理与增强 读取数据 设置Loss 设置模型 设置优化器和学习率调整算法 设置混合精度,DP多卡,EMA 定义训练和验证函数 训练函数 验证函数 调用训练和验证方法 运行以及结果查看

额外说明

wordpress插件_7个最佳WordPress广告管理插件和解决方案

WordPress 插件 您是否正在寻找适用于 WordPress 的最佳广告管理插件和工具?许多 WordPress 网站所有者依靠广告来通过其网站获利。在本文中,我们将分享 WordPress 的最佳广告管理插件和解决方案,以优化您的广告收入。 您是

额外说明

wordpress字体_如何在WordPress帖子编辑器中使用图标字体(无需HTML)

WordPress 字体 Icon fonts are vector icons used as fonts. They are popular among web designers because they look prettier than bi

额外说明

Java 身份证号加星返回

/** * 输出这样格式的身份证:3****************X * @param idStr * @return */ public static String hideIdNumber(Strin

ads via 小工具