springboot mongo查询游标(cursor)不存在错误

背景介绍

  • 线上系统收到不到接口查询失败的告警,均为mongo查询,返回的错误状态码为-5,报错日志如下所示:
2022-02-09 18:16:56.631 ERROR 1 --- [ask-scheduler-6] o.s.integration.handler.LoggingHandler : org.springframework.dao.DataAccessResourceFailureException: 
Query failed with error code -5 and error message 'Cursor 73973161000 not found on server <mongodb-server>' on server <mongodb-server>; 
nested exception is com.mongodb.MongoCursorNotFoundException: 
Query failed with error code -5 and error message 'Cursor 73973161000 not found on server <mongodb-server>' on server <mongodb-server> 
  • 从上面的报错信息来看,是查询在执行过程中游标找不到导致的。可能是被回收或关闭,也可能确实没有。

故障排查及灰度测试

故障排查过程

  • 首先确定是否为完全阻塞式故障,这将决定是否将要紧急修复(封网期发布是有限制的)。经过获取客户端的请求参数进行模拟重试后(仅查询),发现是重试是可以成功的。确定不是必现bug
  • 由于客户端一般是分不同的类型来进行批量查询,发现出错的查询均为某一类的type,它们共有的特点就是查询的数据量比较大。确定可能跟大数据量查询有关
  • 获取线上报错的请求参数,在测试环境mock完数据后进行批量并发测试,并未能重现问题。确定可能跟部署环境有关

灰度过程

  • 测试环境复现不了问题,因此转到线上环境。本次灰度过程采用就k8s pods的金丝雀发布,配置的线上流量为20%
  • 由于代码中有构建mongo线程池,最小的连接数为5,最大空闲时间为30s。一种猜测是可能请求线程在获取到连接并将要查询时,连接超时被回收导致查询失败,因此将线程池的min collection设置为1,确保不会取到将要被回收的连接线程。经过灰度后,仍然会报错
  • 考虑出错的均为大数据量查询,猜测可能与游标超时有关。将出错的type的query设置为noCursorTimeout,经过灰度后,仍然会报错
  • 考虑到可能查询数量超过了mongo的默认batch_size(101行),当数据量太大导致分批迭代时间太长导致超时。因此可以在程序侧修改batch_size大小,增加每次读取的数量。将query的batch_size设置为1000,经过灰度后,不会报错了
  • 考虑可能连接的mongo地址是load balancer,可能mongo在分批加载数据时请求到不同的后端服务器,也即说游标可能由A服务器生成,但是代完数据继续请求数据时访问到 B 服务器,由于该游标不是 B 生成的,因此也会报错。在结合比较测试和生产环境,发现生产连的确实的lb地址,而测试并不是,这就是测试环境重现不出的原因。经过将lb地址改为直连mongos地址,经过灰度后,不会报错了。同时删除掉设置的batch_size也不会再报错

核心原因

关于游标的概念

  • 在springboot项目中,不管是使用java-mongo-driver或者springframework.data.mongodb.core依赖包,当使用 find() 相关函数从 mongo 获取数据时,它返回的并不是数据本身,而是一个游标,且每一个游标都对应一个 id,mongo 服务器会管理这个游标。真正获取数据是用这个游标去 mongo 获取数据;且为了提高 io 利用率,用游标获取数据是批量返回,每一批的大小是由 batch_size 参数决定的,默认是 101 行。真正获取数据的触发时间是在调用 find() 相关函数拿到游标之后,在第一次用 iterator 迭代游标时,客户端会将根据游标拿到的这一批数据放到内存中,然后再用 iterator.next() 一条一条的读取。当内存中的这一批数据迭代完之后,客户端会用这个游标去 mongo 服务器去取下一批数据。
  • 游标是 mongo 服务器生成的,是一种系统资源,类似于线程。所以游标用完了需要及时回收。游标有个超时时间,默认为 10min。在超时时间内,如果客户端使用完游标,则会向服务器发送 close 命令,服务器接口到这个命令之后就会回收游标;另一种情况是,在超时时间内,客户端未使用完游标,则服务器会主动回收游标。在可以设置让服务器永远不回收掉游标。

游标为什么会找不到?

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

游标相关的设置(仅考虑超时情况)

  • 在服务端增大 mongo 服务器的游标超时时间。参数是 cursorTimeoutMillis,其默认是 10 min。修改后需重启 mongo 服务器。
  • 在客户端一次性获取到全部符合条件的数据。也即将batch_size设置为很大的数,但次数若真的有很多数据的话,则对系统内存要求较高,同时如果数据量过大或处理过程过慢依旧会出现游标超时的情况。所以batch_size的评估是一个技术活。
  • 客户端设置游标永不超时:
    • 这种方式的缺点是如果程序意外停止或异常,该游标永远不会被释放,除非重启 mongo,否则会一直占用系统资源,属于危险操作。经过咨询DBA,一般很少对游标的数量进行监控,一般是由其引起的连锁反应如CPU/内存过高才能引起关注,一般的处理方式也就是重启mongo服务器,这样影响就比较大了。
    • 经过查询,在mongo 3.6版本后,客户端就算把游标设置为永不超时。服务端仍然会在闲置30分钟后将其kill掉,所以大量查询若超过30分钟的话需要手动执行下refreshsession来防止超时。但在3.6以下版本则会一直存在。mongo文档
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,063评论 6 510
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,805评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,403评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,110评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,130评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,877评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,533评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,429评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,947评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,078评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,204评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,894评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,546评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,086评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,195评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,519评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,198评论 2 357

推荐阅读更多精彩内容