首页 > 数据库 >mongodb 深度分页优化思路之cursor游标

mongodb 深度分页优化思路之cursor游标

时间:2023-06-23 15:24:22浏览次数:39  
标签:mongodb 游标 cursor import org com public

  mongodb 没有官方的游标滚动实现深度分页功能,建议的都是选择出一个字段,如_id,然后每次查询时限制该字段,而不进行分页处理。

  也没有看到更优的实现方式,本文做一个大胆的假设,自行实现滚动分页功能。供大家思路参考。

  但是猜想可以自行实现一个,简单思路就是,第一次查询时不带limit进行查询全量数据,然后自己通过cursor迭代出需要的行数后返回调用端,下次再调用时,直接取出上一次的cursor,再迭代limit的数量返回。

  优势是只需计算一次,后续就直接复用结果即可。该功能需要有mongodb的clientSession功能支持。

  但是需要复杂的自己维护cursor实例,打开、关闭、过期等。稍微管理不好,可能就客户端内存泄漏或者mongo server内存泄漏。

 

实践步骤:

1. 引入mongo 驱动:

        <!-- https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-sync -->
        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongodb-driver-sync</artifactId>
            <version>4.4.2</version>
        </dependency>
        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongodb-driver-core</artifactId>
            <version>4.4.2</version>
        </dependency>
        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>bson</artifactId>
            <version>4.4.2</version>
        </dependency>

  注意版本不匹配问题,所以要引入多个包。

 

2. 创建测试类:

  验证接入mongo无误,且造入适量的数据。

import static com.mongodb.client.model.Filters.eq;

import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.WriteConcern;
import com.mongodb.client.*;
import com.mongodb.client.result.InsertOneResult;
import org.bson.Document;
import org.junit.Before;
import org.junit.Test;
import org.openjdk.jmh.annotations.Setup;

public class MongoQuickStartTest {

    private MongoClient mongoClient;

    @Before
    public void setup() {
        // Replace the placeholder with your MongoDB deployment's connection string
        String uri = "mongodb://localhost:27017";
        MongoClientSettings options = MongoClientSettings.builder()
                .applyConnectionString(new ConnectionString(uri))
                .writeConcern(WriteConcern.W1).build();
        mongoClient = MongoClients.create(options);
    }

    @Test
    public void testFind() {
//        ConnectionString connectionString = new ConnectionString("mongodb://localhost:27017");
//        MongoClient mongoClient = MongoClients.create(connectionString);
        // Replace the placeholder with your MongoDB deployment's connection string
        MongoDatabase database = mongoClient.getDatabase("local");
        MongoCollection<Document> collection = database.getCollection("test01");
        Document doc = collection.find(eq("name", "zhangsan1")).first();
        if (doc != null) {
            System.out.println(doc.toJson());
        } else {
            System.out.println("No matching documents found.");
        }
    }

    @Test
    public void testInsert() {
        Document body = new Document();
        long startId = 60011122212L;
        MongoDatabase database = mongoClient.getDatabase("local");
        MongoCollection<Document> collection = database.getCollection("test01");
        int i;
        for (i = 0; i < 500000; i++) {
            String id = (startId + i) + "";
            body.put("_id", id);
            body.put("name", "name_" + id);
            body.put("title", "title_" + id);
            InsertOneResult result = collection.insertOne(body);
        }
        System.out.println("insert " + i + " rows");
    }
}

 

3. 创建cursor的查询实现类并调用

  基于springboot创建 controller进行会话测试,使用一个固定的查询语句进行分页测试。

import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.WriteConcern;
import com.mongodb.client.*;
import org.bson.Document;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class MongoDbService {

    private MongoClient mongoClient;
    // 所有游标容器,简单测试,真正的管理很复杂
    private Map<String, MongoCursor<Document>> cursorHolder
            = new ConcurrentHashMap<>();

    public void ensureMongo() {
        // Replace the placeholder with your MongoDB deployment's connection string
        String uri = "mongodb://localhost:27017";
        MongoClientSettings options = MongoClientSettings.builder()
                .applyConnectionString(new ConnectionString(uri))
                .writeConcern(WriteConcern.W1).build();
        mongoClient = MongoClients.create(options);
    }

    // 特殊实现的 cursor 滚动查询
    public List<Document> findDataWithCursor(String searchAfter, int limit) {
        ensureMongo();
        MongoDatabase database = mongoClient.getDatabase("local");
        MongoCollection<Document> collection = database.getCollection("test01");
        List<Document> resultList = new ArrayList<>();
        MongoCursor<Document> cursor = cursorHolder.get(searchAfter);
        if(cursor == null) {
            // 第一次取用需要查询,后续直接复用cursor即可
            cursor = collection.find().sort(new Document("name", 1)).iterator();
            cursorHolder.put(searchAfter, cursor);
        }
        int i = 0;
        // 自行计数,到达后即返回前端
        while (cursor.hasNext()) {
            resultList.add(cursor.next());
            if(++i >= limit) {
                break;
            }
        }
        if(!cursor.hasNext()) {
            cursor.close();
            cursorHolder.remove(searchAfter);
        }
        return resultList;
    }
}

  应用调用controller:

    @Resource
    private MongoDbService mongoDbService;

    @GetMapping("/mongoPageScroll")
    @ResponseBody
    public Object mongoPageScroll(@RequestParam(required = false) String params,
                                  @RequestParam String scrollId) {
        return mongoDbService.findDataWithCursor(scrollId, 9);
    }

  测试方式,访问接口:http://localhost:8080/hello/mongoPageScroll?scrollId=c,然后反复调用(下一页)。

  如此,只要前端第一次查询时,不存在cursor就创建,后续就直接使用原来的结果。第一次可能慢,第二次就很快了。

  结论,是可以简单实现的,但是生产不一定能用。因为,如何管理cursor,绝对是个超级复杂的事,何时打开,何时关闭,超时处理,机器宕机等,很难解决。

标签:mongodb,游标,cursor,import,org,com,public
From: https://www.cnblogs.com/yougewe/p/17499186.html

相关文章

  • springboot & mongodb test
    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId></dependency>下载方式https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-os-x/docker方式do......
  • mongodb-用户管理
    创建用户时,登录到哪个库下,哪个库就是该用户的验证库。在使用该用户远程连接登录时,需要指定验证库才能登录到mongodb中。创建用户>useadminswitchedtodbadmin>db.createUser(...{...user:"root",...pwd:"root123",...roles:[{role:"root",db:"admin......
  • MongoDB数据库特点
    1MongoDB数据库特点1.1易扩展性MongoDB使用分片技术对数据进行扩展,MongoDB能自动分片、自动转移分片里面的数据块,去掉了关系型数据库的关系型特性,数据之间没有关系。让每一个服务器里面存储的数据都是一样大小。这样就非常容易扩展。1.2网站数据具有高性能Mongo非常适合实时的插入,......
  • 宝塔面板 mongodb设置账号,密码,数据库,并配置远程连接
    宝塔面板安装 mongo4.0后需要设置账号密码可数据库才能使用安装后 authorization:disabled  默认配置时才可以使用这个功能,如果没有admin需要先手动创建一个admin库,然后在创建一个  yourdatabaseSSH终端1、进入mongodb安装目录,下面是宝塔面板的默认目录cd/www/......
  • Mongodb GeoJSON 地理数据处理 其实我也很厉害
    相信如果提起地理数据的处理,首先想起的数据库就是postgis,对大名鼎鼎的postgresql + 插件的方式来将POSTGRESQL变成纯纯的地理数据处理的数据库,这是人尽皆知和童叟无欺的功能。 那么世界上如果我不想使用POSTGRESQL的 postgis来处理我的地理数据以外的选择,那么NO.2的选择......
  • MONGODB 奇怪12问 switch over
    开头还是介绍一下群,如果感兴趣polardb,mongodb,mysql,postgresql ,redis等有问题,有需求都可以加群群内有各大数据库行业大咖,CTO,可以解决你的问题。最近我们公司的REDISDBA和MONGODBDBA在SWITCHOVER, 我提出了一些问题关于MONGODB的问题,来让两个DBA能更快的融合对方的......
  • ubuntu安装mongodb
    1.下载mongodbhttps://www.mongodb.com/try/download/community下载ubuntu的tgz版本2.解压tgz解压安装包到工作目录3.修改.bashrcexportMONGODB_HOME=xxx/mongodbexportPATH=$PATH:$MONGODB_HOME/bin4.启动mongodbmongod--dbpath$MONGODB_HOME/data/db--logpath$M......
  • mongodb-安装过程
    1、下载地址https://www.mongodb.com/try/download/community2、安装过程关闭THP,提高性能#vim/etc/rc.localiftest-f/sys/kernel/mm/transparent_hugepage/enabled;thenechonever>/sys/kernel/mm/transparent_hugepage/enabledfiiftest-f/sys/kernel/mm/t......
  • MongoDB学习笔记
    MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似json的bson格式,因此可以存......
  • centos 安装mongodb
    在CentOS系统中安装MongoDB可以按照以下步骤进行:配置MongoDB的Yum仓库sudovi/etc/yum.repos.d/mongodb-org-4.4.repo在编辑器中输入以下内容:[mongodb-org-4.4]name=MongoDBRepositorybaseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.......