mybatis中Cursor的处理百万数据

MyBatis中的Cursor 百万级数据的应用

一、简介

MyBatis中的Cursor是一种用于处理大量数据查询的特殊类型。它允许我们在查询结果集中逐行获取数据,而不是一次性将所有数据加载到内存中。这对于处理大型数据集非常有用,可以减少内存消耗并提高查询性能。

二、优点和使用

正对大数据的查询操作时,如果我们一次性,例如将一百万的数据加载到内存中很大可能会导致我们的系统发生内存溢出的错误,这个时候我们就可以考虑使用MyBatis中的Cursor流式查询,Cursor它并不会一次性的将我们在数据库中查询到的数据全部返回过来,而是先将一部分的数据存入到内存里面,然后通过迭代的方式在获取数据,Cursor实现了迭代器iterator接口,我们可以通过next方法获取数据。一般我们在需要查询大量数据进行操作时候就可以使用Cursor提升效率,降低内存溢出的分险。例如我们需要将大量的数据导入execl的时候我们就可以使用Cursor 实现。

三、cursor测试案例

我们可以测试一下先创建一个表test_table

CREATE TABLE test_table (
  id INT PRIMARY KEY,
  name VARCHAR(20),
  age INT,
	address VARCHAR(200)
);

像表中插入十万的数据

-- 创建一个存储过程,用于插入10万测试数据
DELIMITER //
CREATE PROCEDURE insert_test_data()
BEGIN
  DECLARE i INT DEFAULT 1;
  WHILE i <= 100000 DO
    -- 随机生成姓名和年龄
    SET @name = CONCAT('name', i);
    SET @address = CONCAT('address......................', i);
    SET @age = FLOOR(RAND() * 100);
    -- 插入数据
    INSERT INTO test_table (id, name, age, address) VALUES (i, @name, @age, @address);
    -- 更新计数器
    SET i = i + 1;
  END WHILE;
END //
DELIMITER ;

调用存储过程

-- 调用存储过程
CALL insert_test_data();

编写两个测试的方法一个是普通的查询,另一个是流式查询

返回Cursor的接口,插入了10万条数据,经历10次union all之后变成了一百万数据了。

    @Select("select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table")
    @Options(resultSetType = ResultSetType.FORWARD_ONLY)
    Cursor<Person> selectAll();

放回正常list的接口

    @Select("select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table union all " +
        "select * from mogu_blog.test_table")
    List<Person> selectAllList();

编写一个测试程序

public class DemoTest {


  private SqlSessionFactory sqlSessionFactory;
    /**
   *Cursor测试
   */
  @Test
  public void testMybatisCursor() throws IOException, InterruptedException {
    Thread.sleep(20000);
    long start = System.currentTimeMillis();
    SqlSession sqlSession = openSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    Cursor<Person> persons = mapper.selectAll();
    int total = 0;
    for (Person person : persons) {
      total++;
    }
    long end = System.currentTimeMillis();
    System.out.println("testMybatisCursor总条数>>>>>>>>>>>>>>>>>>>>"+total);
    System.out.println("testMybatisCursor方法结束时间>>>>>>>>>>>>>>>" + (end - start));
    Thread.sleep(1000);
  }

  /**
   * 正常list测试
   */
  @Test
  public void testMybatisList() throws IOException, InterruptedException {
    Thread.sleep(20000);
    long start = System.currentTimeMillis();
    SqlSession sqlSession = openSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<Person> people = mapper.selectAllList();
    long end = System.currentTimeMillis();
    System.out.println("testMybatisList总条数>>>>>>>>>>>>>>"+people.size());
    System.out.println("方法结束时间>>>>>>>>>>>>>>>>>>>>>>>>" + (end - start));
    Thread.sleep(1000);

  }

  @Before
  public void init()  {
    Reader resourceAsReader = null;
    try {
      resourceAsReader = Resources.getResourceAsReader("org/apache/ibatis/dao/mapper/mybatis-config.xml");
      sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
      resourceAsReader.close();
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }
  
  public SqlSession openSqlSession() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    return sqlSession;
  }

}

我们看一下结果

testMybatisCursor的结果 ,结束时间是3040

testMybatisCursor总条数>>>>>>>>>>>>>>>>>>>>1000000
testMybatisCursor方法结束时间>>>>>>>>>>>>>>>3040

testMybatisList 的结果 3903

testMybatisList总条数>>>>>>>>>>>>>>1000000
方法结束时间>>>>>>>>>>>>>>>>>>>>>>>>3903

目前我们是没有限制内存的,我们可以设置一下堆内存看看,我们将堆内存配置设置为 -Xms500m -Xmx500m 试试看看什么效果

testMybatisCursor的结果 ,结束时间是2489

testMybatisCursor总条数>>>>>>>>>>>>>>>>>>>>1000000
testMybatisCursor方法结束时间>>>>>>>>>>>>>>>2489

testMybatisList 的结果内存溢出了

java.lang.OutOfMemoryError: GC overhead limit exceeded

由上面可以看出Cursor流式查询确实可以提升我们系统的性能和降低内存溢出的可能。由于Cursor是需要事务控制的,如果在系统中出现数据大量的修改等操作的话不建议使用Cursor,Cursor尽量使用小事务的情况。

  • 13
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

久★伴i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值