MongoTemplate查询海量数据

本文探讨了在处理大量数据时,如何通过游标和分页方式从MongoDB中分批获取数据,避免内存溢出,并对比了两种方法在不同数据量下的效率。作者提供了实际代码示例和优化建议,强调游标的自动批处理特性以及分页的适用场景。
摘要由CSDN通过智能技术生成

在进行数据迁移时,需要从mongodb中获取数据,而数据有比较多,所以不能一次性取出所有数据,需要分批获取数据。分批获取数据主要有两种方式:

如下:

    private static final int BATCH_SIZE = 1000;
    private static final int PAGE_LIMIT = 1000;
    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 游标方式查询
     */
    public void getMongoListByCursor(){
//条件
        Criteria criteria = Criteria.where("typeName").is("rdbms_table")
                .and("status").is("ACTIVE")
                .and("attributes.versionType").is("formal");
       Query query = new Query(criteria);
       //noCursorTimeout(true):设置游标查询不超时;
        MongoCursor<Document> iterator = mongoTemplate.getCollection("entities").find(query.getQueryObject()).noCursorTimeout(true).batchSize(BATCH_SIZE).iterator();
        //下面这样写不行,find方法中,不要加实体类型,会报错。
//      MongoCursor<Document> iterator = mongoTemplate.getCollection("entities").find(query.getQueryObject(),Entity.class).noCursorTimeout(true).batchSize(BATCH_SIZE).iterator();
        List<MetadataTableEntity> metadataTableEntityList = new ArrayList<>();
        metadataTableEntityList.forEach(System.out::println);

        while (iterator.hasNext()){
            Document document = iterator.next();
            Entity entity = JSON.parseObject(JSON.toJSONString(document), Entity.class);
            MetadataTableEntity metadataTableEntity = JSON.parseObject(JSON.toJSONString(entity.getAttributes()), MetadataTableEntity.class);
            metadataTableEntity.setCreateTime(entity.getCreateTime());
            metadataTableEntity.setUpdatedTime(entity.getUpdatedTime());
            metadataTableEntity.setGuid(entity.getGuid());
            metadataTableEntityList.add(metadataTableEntity);

        }
        metadataTableEntityList.forEach(System.out::println);
    };

    /**
     * 分页方式查询skip+limit
     */
    public void getMongoListByPage(){
        Criteria criteria = Criteria.where("typeName").is("rdbms_table")
                .and("status").is("ACTIVE")
                .and("attributes.versionType").is("formal");
        Query query = new Query(criteria);
        long amount = mongoTemplate.count(query, Entity.class,"entities");
        int page = (int)amount / PAGE_LIMIT;
        if(amount % PAGE_LIMIT > 0 ){
            //余数不为0时,要加1
            page += 1;
        }
        query.limit(PAGE_LIMIT);
        List<MetadataTableEntity> metadataTableEntityList = new ArrayList<>();
        for(int i =0; i < page;i++){
            query.skip(i * PAGE_LIMIT);
            List<Entity> entitieList = mongoTemplate.find(query, Entity.class,"entities");
            entitieList.forEach(entity -> {
                MetadataTableEntity metadataTableEntity = JSON.parseObject(JSON.toJSONString(entity.getAttributes()), MetadataTableEntity.class);
                metadataTableEntity.setCreateTime(entity.getCreateTime());
                metadataTableEntity.setUpdatedTime(entity.getUpdatedTime());
                metadataTableEntity.setGuid(entity.getGuid());
                metadataTableEntityList.add(metadataTableEntity);
            });
        }
        metadataTableEntityList.forEach(System.out::println);
    };
 

以下是测试的结果:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

但是发现有点问题:

既然我想分批处理,就是因为怕内存不足,但是游标方式会一条条处理结果,或者会把所有的结果都放入一个list中,一条条处理不满足要求,那么问题来了,如果使用list,这个list如果接收不了这么多数据怎么办?

解决办法:

  1. 使用游标设置变量计数,达到大小或者结束了,就批处理一下。
  2. 使用分页形式进行处理。

测试结论:

  1. 数据量少的话,游标和分页方式差不多,数据量大的话,游标有优势,skip方式会越来越慢。
    
  2. 当你遍历游标到返回的批处理结束,如果有更多的结果,cursor.next()将自动执行更多的操作来获取下一批。
  3. 使用游标方式有限制,查询出来的结果需要一条条处理,如果查询出来的数据也要分批次批量处理,要么使用分页查询方式,要么设置一个变量计数,进行处理。
  4. 由于数量有限,可能没有真正发挥游标的真正作用,不知道是不是游标方式会自动检测内存大小,超过内存大小,会自动返回结果,然后继续接着的游标往下处理。

最后,如果有了解游标使用方法的朋友,可以来指教一下,谢谢!

参考连接:
https://blog.csdn.net/jinbang1991/article/details/115343888?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-3.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-3.no_search_link

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值