MongoDB 排序内存超过 32M 限制

2020-05-22 00:00:00 索引 数据 数据库 内存 排序

今天群里有个小伙伴问了个问题。

对某个字段 sort 后,再 skip 一个超大值,之后 MongoDB 会报错,因为用于排序的内存超过了 32m。如果 skip 一个小值,就不会报错。

问题是,这个排序字段是没有索引的,那么 sort 时数据都是放在内存里面 sort。为啥 skip 小就没事,skip 大就会撑爆内存呢?

小伙伴说,已经问了 10 个群了,没人回答为啥。


嗯?

skip 一个超大值,然后占用很多内存报错,这不是显而易见的吗。

几乎每本数据库的书都会提到,不要 skip 一个超大值,会产生性能问题,然后给出几个替代方案。

仔细一想,有古怪。由于没有索引,数据拿到内存里面进行排序,那 skip 多和少应该没有影响。但是事实上,skip 超大值的确会导致内存超出限制。

Google 了半天,全是怎么处理这个问题,没有提到为什么产生这个现象。

经过谨慎的思(nao)考(bu),我提出了一个想法:

  1. 问题的根源显然出现在 MongoDB 的排序算法上,但是本人未能找到其算法实现
  2. 如果我来实现排序算法,我会将所有数据全部读取后,再进行排序吗?NO
  3. 在一个存在 limit、skip 的查询语句中,数据库排序后需要临时保存的数据量是 limit + skip 条数据。
  4. 那数据库的排序操作,很可能是先获取 limit+skip 条数据作为一个 chunk,排序后,开始逐一获取剩余数据。之后每条新数据与 chunk 内数据进行比较,符合要求则进入,并踢出 chunk 内对应数据。
  5. 所以,当 skip 超大时,chunk 的数据量也超大,后超过了 32m 从而报错。

以上想法能够解释问题现象,但是没有文档依据。

好吧,那我们去看看官方文档:

Add an index, or specify a smaller limit
In MongoDB, sort operations can obtain the sort order by retrieving documents based on the ordering in an index. If the query planner cannot obtain the sort order from an index, it will sort the results in memory. Sort operations that use an index often have better performance than those that do not use an index. In addition, sort operations that do not use an index will abort when they use 32 megabytes of memory.
翻译过来说,就是在mongodb中,sort操作能通过索引获得在要排序的documents中的顺序,如果执行计划中没有从索引中获取顺序,它就会在内存中对检索结果进行排序。 使用索引的Sort操作在性能上往往比没有使用索引的sort要好。另外。没有使用索引的sort操作在使用了32M内存的时候会被终止掉(使用内存排序时,限制查询结果默认多为32M,超过的话查询则会被终止)。关于mongoDB使用sort排序引发的生产bug

对问题的建议是添加索引,或者减少 limit,描述中限制查询结果多为 32m。

以上文档能在一定程度上佐证我的想法。

相关文章