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

背景

使用mongo查询过期数据记录,由于业务量增加导致过期数据激增,之前运行正常的任务报游标超时错误。

2021-08-16 01:12:39.119 ERROR scheduling-1 [org.springframework.scheduling.support.TaskUtils$LoggingErrorHandler:96]-Unexpected error occurred in scheduled task.
com.mongodb.MongoCursorNotFoundException: Query failed with error code -5 and error message 'Cursor 947173600818248061 not found on server 192.168.109.138:20000' on server 192.168.109.138:20000
	at com.mongodb.operation.QueryHelper.translateCommandException(QueryHelper.java:27) ~[mongodb-driver-core-3.8.2.jar!/:?]
	at com.mongodb.operation.QueryBatchCursor.getMore(QueryBatchCursor.java:229) ~[mongodb-driver-core-3.8.2.jar!/:?]
	at com.mongodb.operation.QueryBatchCursor.hasNext(QueryBatchCursor.java:115) ~[mongodb-driver-core-3.8.2.jar!/:?]
	at com.mongodb.client.internal.MongoBatchCursorAdapter.hasNext(MongoBatchCursorAdapter.java:54) ~[mongodb-driver-3.8.2.jar!/:?]

原因分析

MongoCursor<Document> mongoCursor = mongoClient.getCollection(fileRecordCollection).find(expireFileQuery).iterator()

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

解决方案

批量读取固定数量的记录,保证10分钟内完成记录的处理(推荐)

MongoCursor<Document> mongoCursor = mongoClient.getCollection(fileRecordCollection).find(expireFileQuery)
                    .batchSize(SysConstant.TENTHOUSAND).iterator();

这种方案比较稳妥,但是会增加数据库的连接次数,从而增加I/O耗时。如果连接创建次数在接受范围内,是完全可以的。

设置游标永不超时(不推荐)

MongoCursor<Document> mongoCursor = mongoClient.getCollection(fileRecordCollection).find(expireFileQuery).noCursorTimeout(true).iterator();

让游标永不超时,然而这个操作非常危险,因为如果循环参数异常,甚至使用mongo的应用服务器断电或断网,都会导致MongoDB服务器资源永远无法被释放。这个游标就再也无法关闭了!除非重启MongoDB,否则这些游标会一直留在MongoDB上,占用资源。
可以使用try catch finally去做。

/ * try catch:自己处理异常
  * try {
  *可能出现异常的代码
  *} catch(异常类名A e){
  *如果出现了异常类A类型的异常,那么执行该代码
  *} ...catch可以有多个)
  * finally {
  *最终肯定必须要执行的代码(例如释放资源的代码)
  *}

虽然这种方法会对代码美观性造成一定的破坏但是无疑解决了我们的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

融极

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值