mongodb游标错误:com.mongodb.MongoCursorNotFoundException: Query failed with error code -5

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013066244/article/details/82658912

环境

mongodb:3.4.15
java:1.7

场景

对用户日志表补加时间戳的字段;

具体代码如下:

public static void main(String[] args) {
        final MongoCollection<Document> useropRecord;
        //连接数据库 start
        MongoCredential credential = MongoCredential.createCredential("gg_user_db_rw", "gg_user_db", "gg_user_db_rw.gogoal.com".toCharArray());
        ServerAddress serverAddress;
        serverAddress = new ServerAddress("106.75.51.20", 35724);//35724
        List<ServerAddress> addrs = new ArrayList<ServerAddress>();
        addrs.add(serverAddress);
        List<MongoCredential> credentials = new ArrayList<MongoCredential>();
        credentials.add(credential);
        @SuppressWarnings("resource")
        MongoClient mongoClient = new MongoClient(addrs, credentials);
        System.out.println("Connect to database successfully");
        //连接数据库 end

        MongoDatabase database = mongoClient.getDatabase("gg_user_db");

        useropRecord = database.getCollection("userop_record");//埋点表

        Document match = new Document();
        match.append("_tm", null);

        Date stringToDate = DateUtil.stringToDate("2017-01-01", "yyyy-MM-dd");
        match.append("date", new Document("$gte", stringToDate));
        match.append("code", "S3_06");
        useropRecord.find(match).forEach(new Block<Document>() {
            int aa=3000;
            @Override
            public void apply(Document doc) {
                Document project = new Document();
                project.append("$set", new Document("_tm", new BSONTimestamp((int)(System.currentTimeMillis() / 1000), aa++)));
                useropRecord.updateMany(new BasicDBObject("_id", doc.get("_id")), project);
                if(aa >= 4000){
                    aa = 3000;
                }
              }
            }
        );
    }

上面这段代码 执行着就会报如下错误:

Exception in thread "main" com.mongodb.MongoCursorNotFoundException: Query failed with error code -5 and error message 'Cursor 5741457193246430646 not found on server 106.75.51.20:35724' on server 106.75.51.20:35724
    at com.mongodb.operation.QueryHelper.translateCommandException(QueryHelper.java:27)
    at com.mongodb.operation.QueryBatchCursor.getMore(QueryBatchCursor.java:213)
    at com.mongodb.operation.QueryBatchCursor.hasNext(QueryBatchCursor.java:103)
    at com.mongodb.MongoBatchCursorAdapter.hasNext(MongoBatchCursorAdapter.java:46)
    at com.mongodb.OperationIterable.forEach(OperationIterable.java:72)
    at com.mongodb.FindIterableImpl.forEach(FindIterableImpl.java:166)
    at test.MongodbTimestamp.main(MongodbTimestamp.java:54)

这段错误的原因呢,这里引用网上的解释:

你在用 db.collection.find() 的时候,它返回的不是所有的数据,而实际上是一个“cursor”。它的默认行为是:第一次向数据库查询 101 个文档,或 1 MB 的文档,取决于哪个条件先满足;之后每次
cursor 中的文档用尽后,查询 4 MB 的文档。另外,find() 的默认行为是返回一个 10 分钟无操作后超时的 cursor。如果我一个 batch 的文档十分钟内没处理完,过后再处理完了,再用同一个 cursor id 向服务器取下一个 batch,这时候
cursor id 当然已经过期了,这也就能解释为啥我得到 cursor id 无效的错误了。

Stack Overflow 上有人提出过解决方法,是在 find() 时传入 timeout=False 来禁用 10 分钟超时的保护措施。但是我觉得这是非常差的办法,因为如果你循环时产生异常,甚至断电或断网,都会导致 MongoDB 服务器资源永远无法被释放。而更好的办法是(我也发在了 Stack Overflow 上),估计一个 batch 大小,让 MongoDB 客户端每次抓取的文档在 10 分钟内能用完,这样客户端就不得不 10 分钟内至少联系服务器一次,保证cursor 不超时。

具体做法:useropRecord.find(match).batchSize(10000)

修改后的代码

public static void main(String[] args) {
        final MongoCollection<Document> useropRecord;
        //连接数据库 start
        MongoCredential credential = MongoCredential.createCredential("gg_user_db_rw", "gg_user_db", "gg_user_db_rw.gogoal.com".toCharArray());
        ServerAddress serverAddress;
        serverAddress = new ServerAddress("106.75.51.20", 35724);//35724
        List<ServerAddress> addrs = new ArrayList<ServerAddress>();
        addrs.add(serverAddress);
        List<MongoCredential> credentials = new ArrayList<MongoCredential>();
        credentials.add(credential);
        @SuppressWarnings("resource")
        MongoClient mongoClient = new MongoClient(addrs, credentials);
        System.out.println("Connect to database successfully");
        //连接数据库 end

        MongoDatabase database = mongoClient.getDatabase("gg_user_db");

        useropRecord = database.getCollection("userop_record");//埋点表

        Document match = new Document();
        match.append("_tm", null);

        Date stringToDate = DateUtil.stringToDate("2017-01-01", "yyyy-MM-dd");
        match.append("date", new Document("$gte", stringToDate));
        match.append("code", "S3_06");
        useropRecord.find(match).batchSize(10000).forEach(new Block<Document>() {
            int aa=3000;
            @Override
            public void apply(Document doc) {
                Document project = new Document();
                project.append("$set", new Document("_tm", new BSONTimestamp((int)(System.currentTimeMillis() / 1000), aa++)));
                useropRecord.updateMany(new BasicDBObject("_id", doc.get("_id")), project);
                if(aa >= 4000){
                    aa = 3000;
                }
              }
            }
        );
    }

这里我估计的一个大小是10000,之前是我写5万,依然会报错,后来改3万,还是会报错。
最后我测试看了下,大概10分钟 数据库就更新1万条数据,所以改成1万了。

从目前实战的效果来看,昨天跑了一下午,今天又跑了一上午,都没有报这个错误了。

参数地址:

MongoDB 游标超时解决办法

猜你喜欢

转载自blog.csdn.net/u013066244/article/details/82658912