赞
踩
系统中有个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万 | 7s | 4s |
400万 | 7分 | 2分 |
实际在测试的过程中,发现删除数据其实花费时间很少,大概200多毫秒删1万条,但MongoCursor在遍历循环的时候比较耗时while (dbCursor.hasNext()) { }。
而mongoTemplate.find在查询的数据的时候,如果数据有索引,每次查询1万条其实速度是挺快的,根据找到符合条件的1万条数据,立即返回。(顺便说下,mongoTemplate.count计算总数比较慢)
遇到类似的问题,可以结合业务,对两种方案进行测试,一般第二种方案效率更高一点。
有其他更好的方案欢迎交流!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。