irpas技术客

mongo cursor 游标超时_红衣女妖仙_mongo游标超时

网络投稿 937

mongo cursor 游标超时

mongo cursor 游标超时问题


场景描述

使用 java 程序通过定时任务处理 mongo 中的数据时(mongo 集合中共有近一亿个文档,每次只处理其中的小二十万),出现了以下错误:

org.springframework.data.mongodb.UncategorizedMongoDbException: Query failed with error code 43 and error message 'Cursor not found, cursor id: 412824332348' on server ip:30000; nested exception is com.mongodb.MongoQueryException: Query failed with error code 43 and error message 'Cursor not found, cursor id: 412824332348' on server ip:30000 or org.springframework.dao.DataAccessResourceFailureException: Query failed with error code -5 and error message 'Cursor 3998850069977484811 not found on server 172.29.181.137:30000' on server ip:30000; nested exception is com.mongodb.MongoCursorNotFoundException: Query failed with error code -5 and error message 'Cursor 3998850069977484811 not found on server 172.29.181.137:30000' on server ip:30000 关于游标(cursor)

当使用 find() 相关函数从 mongo 获取数据时,它返回的并不是数据本身,而是一个游标,且每一个游标都对应一个 id,mongo 服务器会管理这个游标。正真获取数据是用这个游标去 mongo 获取数据;且为了提高 io 利用率,用游标获取数据是批量返回,每一批的大小是由 batch_size 参数决定的,默认是 101 行。正真获取数据的触发时间是在调用 find() 相关函数拿到游标之后,在第一次用 iterator 迭代游标时,客户端会将根据游标拿到的这一批数据放到内存中,然后再用 iterator.next() 一条一条的读取。当内存中的这一批数据迭代完之后,客户端会用这个游标去 mongo 服务器去取下一批数据。

且,游标是 mongo 服务器生成的,是一种系统资源,类似于线程。所以游标用完了需要及时回收。游标有个超时时间,默认为 10min。在超时时间内,如果客户端使用完游标,则会向服务器发送 close 命令,服务器接口到这个命令之后就会回收游标;另一种情况是,在超时时间内,客户端未使用完游标,则服务器会主动回收游标。

// 切换数据库 MongoDatabase mongoDatabase = MongoUtil.getMongoClient("test"); // 获取集合实例 MongoCollection mongoCollection = mongoDatabase.getCollection("collection_name"); // 组装查询条件 BasicDBObject query = new BasicDBObject(); query.put("username", "momo"); // 查询 此时返回游标 FindIterable<Document> findIterable = mongoCollection.find(query); // 获取迭代器 MongoCursor<Document> mongoCursor = findIterable.iterator(); Document document; while (mongoCursor.hasNext()) { document = mongoCursor.next(); } 游标为什么会找不到?

游标找不到通常有以下两种情况:

1、客户端游标超时,被服务端回收,再用游标向服务器请求数据时就会出现游标找不到的情况。2、在 mongo 集群环境下,可能会出现游标找不到的情况。 游标由 mongo 服务器生成,在集群环境下,当使用 find() 相关函数时返回一个游标,假设此时该游标由 A 服务器生成,迭代完数据继续请求数据时,访问到了 B 服务器,但是该游标不是 B 生成的,,此时就会出现游标找不到的情况。 正常情况下,在 mongo 集群时,会将 mongo 地址以 ip1:port1,ip2:port2,ip3:port3 形式传给 mongo 驱动,然后驱动能够自动完成负载均衡和保持会话转发到同一台服务器,此时不会出现游标找不到的情况。 但当我们自己搭建了负载均衡层,且用 xxx.xxx.com:port 这种方式来配置时,就会出现游标找不到的情况。 解决方案

针对上面游标找不到的两种情况,一般都是以 ip1:port1,ip2:port2,ip3:port3 这种形式配置的,所以这里只讨论第一种情况。

第一种情况的表象是客户端对游标的使用超过了游标超时时间。

1、在服务端增大 mongo 服务器的游标超时时间。参数是 cursorTimeoutMillis,其默认是 10 min。修改后需重启 mongo 服务器。

// 启动时设置 mongod -setParameter cursorTimeoutMillis=60000 // 在线修改 db.runCommand({setParameter:1,cursorTimeoutMillis:60000})

2、在客户端减少游标每次返回的数据量,使每批数据能在 10 min 内消费完。参数是 batchSize,默认值是 101。 这种方式的缺点是增加了 mongo 连接次数,比较消耗 io。

// com.mongodb.client FindIterable<Document> findIterable = mongoCollection.find(query).batchSize(50); // spring-boot-starter-data-mongodb mongoTemplate.find(query.cursorBatchSize(50), Object.class, "collection_name"); // 注:当 batchSize 设置为 -1 时只返回一条数据

3、在客户端一次性获取到全部符合条件的数据。可以通过两种方式设置,分别是 batchSize 和 exhaust。 这种方式的缺点是,由于一次性返回了所有数据,对系统内存要求较高。

// batchSize batchSize(Integer.MAX); cursorBatchSize(Integer.MAX); // 如果数据量过大或处理过程过慢依旧会出现游标超时的情况 可以配合 noCursorTimeout 来解决 即 batchSize(Integer.MAX).noCursorTimeout(); cursorBatchSize(Integer.MAX).noCursorTimeout(); // exhuast 作用是在一个游标批次中返回全量数据 mongoTemplate.find(query.exhuast(), Object.class, "collection_name"); // 使用 exhaust 时需注意以下几点 // 1、mongo cluster 不支持此设置 // 2、EXHAUST 与 limit() 不兼容 // 3、使用此模式时需忽略网络超时情况

4、客户端设置游标永不超时。参数 noCursorTimeout 为 true。 这种方式的缺点是,如果程序意外停止或异常,该游标永远不会被释放,除非重启 mongo,否则会一直占用系统资源,属于危险操作。

// com.mongodb.client FindIterable<Document> findIterable = mongoCollection.find(query).noCursorTimeout(true); // spring-boot-starter-data-mongodb mongoTemplate.find(query.noCursorTimeout(), Object.class, "collection_name");

@XGLLHZ-XXX.mp3


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #mongo游标超时 #mongo #Cursor #游标超时mongo #游标超时问题场景描述使用 #JAVA #程序通过定时任务处理