mongodb大批量删除数据的方案对比

本文探讨了在处理MongoDB大量数据时,使用游标逐条删除和分批查询删除的方案性能差异。方案一借助游标遍历,适合无索引场景;方案二利用query限流并配合索引,效率更高。通过实测对比,建议结合业务需求选择高效删除方式。
摘要由CSDN通过智能技术生成

业务背景

系统中有个mongodb集合每天增长上千万,时间长了,系统中这个集合已经有几亿数据了,需要写一个定时任务,把集合中三个月前的数据删除,并且后面每天凌晨执行这个定时任务.

方案分析

方案一

使用Mongo的游标MongoCursor,遍历获取集合id获取id列表idList,然后根据idList批量删除

方案二

使用mongoTemplate.find,每次查询1万条数据,遍历获取集合id获取id列表idList,然后根据idList批量删除

代码片段

方案一

    /**
     * 删除集合数据
     *
     * @param time 传三个月前的时间戳
     */
    private void deleteData(Long time) {

        Query query = new Query(new Criteria("ct").lt(time));

        MongoCursor<Document> dbCursor = mongoTemplate.getCollection(COLLECTION_NAME)
                .find(query.getQueryObject())
                //设置游标查询不超时
                .noCursorTimeout(true)
                //设置批量从数据库中获取的数据量
                .batchSize(10000)
                .iterator();

        int i = 0;
        List<String> idList = new ArrayList<>();
        Document next;
        while (dbCursor.hasNext()) {
            i++;
            next = dbCursor.next();
            String id = next.get("_id").toString();
            idList.add(id);

            if (i % 10000 == 0) {

                List<String> idListToDelete = new ArrayList<>();
                idListToDelete.addAll(idList);

                // 清空数据
                idList.clear();

                log.info("delete: {}", i);

                // 异步删除数据
                threadPool.execute(new Runnable() {
                    @Override
                    public void run() {
                        doDeleteData(idListToDelete);
                    }
                });
            }
        }

        // 再次判断
        if (CollectionUtils.isNotEmpty(idList)) {
            doDeleteData(idList);
            idList.clear();
        }

        log.info("data delete total: {}", i);
    }

方案二

    /**
     * 删除集合数据
     *
     * @param time 传3个月前时间戳
     */
    private void deleteData(Long time) {

        Query query = new Query(new Criteria("ct").lt(time));
        // 只需要去_id字段即可,避免取多个字段,数据量大了占内存过多
        query.fields().include("_id");
        query.limit(10000);

        List<XXX> xxxList = mongoTemplate.find(query, XXX.class);
        List<String> idListToDelete;
        int i = 0;
        while (CollectionUtils.isNotEmpty(xxxList)) {
            i += xxxList.size();
            idListToDelete = xxxList.stream().map(o -> o.getId()).collect(Collectors.toList());
            doDeleteData(idListToDelete);
            log.info("delete: {}", i);

            xxxList = mongoTemplate.find(query, XXX.class);
        }

        log.info("delete total: {}", i);
    }
    /**
     * 执行删除数据
     */
    private void doDeleteData(List<String> idList) {
        long s0 = System.currentTimeMillis();
        Query query = new Query(new Criteria("_id").in(idList));
        mongoTemplate.remove(query, XXX.class);
        long s1 = System.currentTimeMillis();
        log.info("delete data size: {}, cost: {}", idList.size(), (s1 - s0));
    }

对比

两种方案都是可行的,只不过是速度上的差别,大致速度如下(当时没有记录数据,大致是这个速度)

发送量方案一(游标)方案二(query)
10万7s4s
400万7分2分

总结

实际在测试的过程中,发现删除数据其实花费时间很少,大概200多毫秒删1万条,但MongoCursor在遍历循环的时候比较耗时while (dbCursor.hasNext()) { }。

而mongoTemplate.find在查询的数据的时候,如果数据有索引,每次查询1万条其实速度是挺快的,根据找到符合条件的1万条数据,立即返回。(顺便说下,mongoTemplate.count计算总数比较慢)

遇到类似的问题,可以结合业务,对两种方案进行测试,一般第二种方案效率更高一点。

有其他更好的方案欢迎交流!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值