线上服务的MongoDB中有一个很大的表,我查询时使用了sort()根据某个字段进行排序,结果报了下面这个错误:

[Error] Executor error during find command :: caused by :: Sort operation used more than the maximum 33554432 bytes of RAM. Add an index, or specify a smaller limit.
at line 0, column 0

这是个非常常见的MongoDB报错了。因为MongoDB处理排序时,如果排序的字段没有建立索引,会把全表都丢到内存中处理。

If MongoDB cannot use an index or indexes to obtain the sort order, MongoDB must perform a blocking sort operation on the data. A blocking sort indicates that MongoDB must consume and process all input documents to the sort before returning results.

而内存的大小并不是无限使用的,MongoDB的默认设置是32MB。一旦数据量超过32MB,则会报错。

参数internalQueryExecMaxBlockingSortBytes

32MB这个限制是在参数internalQueryExecMaxBlockingSortBytes中控制。你可以在MongoDB的客户端上直接查看这个参数的值,执行以下语句:

db.runCommand({getParameter: 1,"internalQueryExecMaxBlockingSortBytes": 1
})

返回如下结果:

// 1
{"internalQueryExecMaxBlockingSortBytes": NumberInt("33554432"),"ok": 1,"operationTime": Timestamp(1651142670, 1),"$clusterTime": {"clusterTime": Timestamp(1651142670, 1),"signature": {"hash": BinData(0, "X09M2FBji5f+FOwaK/nLTv4+Ybs="),"keyId": NumberLong("7080087363631710209")}}
}

所以解决排序时内存使用超过32MB的问题,有两个方法:

  1. 给排序的字段加索引。具体怎么加索引,会在后面细讲。
  2. 修改internalQueryExecMaxBlockingSortBytes参数的大小,使用命令如下:
db.adminCommand({setParameter: 1,internalQueryExecMaxBlockingSortBytes: 104857600
})

MongoDB 4.3的internalQueryMaxBlockingSortMemoryUsageBytes

我准备在本地的MongoDB上复现这个问题,于是把这个表直接导入到本地MongoDB中。结果发现排序时并没有报错。使用上面的命令查看internalQueryExecMaxBlockingSortBytes参数的值时,返回如下结果:

[17][ProtocolError] no option found to get

Google了一下,发现了MongoDB的官方网站上的两个相关JIRA。

第一个JIRA [SERVER-44053] Rename setParameter for maximum memory usage of blocking sort - MongoDB Jira里表示,在4.3.1版本时,因为参数命名描述不清楚,所以将参数internalQueryExecMaxBlockingSortBytes改为了internalQueryMaxBlockingSortMemoryUsageBytes。这解释了为什么我执行查询参数的语句时,没有返回结果。

第二个JIRA [SERVER-50767] internalQueryExecMaxBlockingSortBytes causing config exception on mongod load - Mongo中,Comments里提到了,新的internalQueryMaxBlockingSortMemoryUsageBytes参数,默认值从32MB改成了100MB。也许我的这个表使用100MB内存进行排序就够用了,所以没有报错。

所以在4.3以上的版本(本机是5.0.4),执行以下命令:

db.runCommand({getParameter: 1,"internalQueryMaxBlockingSortMemoryUsageBytes": 1
})

可以看到查询结果:

{"internalQueryMaxBlockingSortMemoryUsageBytes": NumberInt("104857600"),"ok": 1
}

而服务器上的MongoDB版本为4.0.3,因此是爆出来最上面的问题。

排序字段如何加索引?

这是个很简单的问题,你用哪个字段排序,就对哪个字段加索引就好了。比如我要根据A字段进行排序,则增加A字段的索引。

-- 加索引
db.bigMongoTable.createIndex({"A": 1
});
-- 查询
db.bigMongoTable.find({}).sort({"A": 1
});

但是如果我改主意了,我要根据A、B两个字段做排序:

db.bigMongoTable.find({}).sort({"A": 1,"B": 1
});

那么熟悉的报错就又回来了。

是的!机智的MongoDB并不会像我们想的那样,先用上A的索引,从而省点力气。他依旧会把全部的数据丢到内存里排序……

那我再加个B字段的索引吧,毕竟在MongoDB查询的时候,对两个字段分别建单键索引,灵活性比直接建一个复合索引要好一些,而且MongoDB的索引交集也可以让这两个单键索引实现和复合索引一样的效果。

哦,不行哟,还是那个报错。

所以,当多字段排序时,你必须要建一个包含了这些字段的复合索引,且要注意以下几点:

  1. 查询时参与排序的多个字段的顺序,要和创建的索引每个字段的顺序保持一致。比如你创建的索引是:db.bigMongoTable.createIndex({"A":1,"B":1,"C":1});那么你的排序语句也要按照顺序如下:sort({"A":1,"B":1,"C":1})。如果你调换A和B的顺序,如下:sort({"B":1,"A":1,"C":1}),则索引不会生效。
  2. 参与查询的字段少于索引的字段,则要保证符合前缀匹配。还是第一点里的索引,如果排序语句是这样:sort({"A":1,"B":1}),则索引继续生效。如果是这样:sort({"A":1,"C":1}),则无法生效。这个你可以理解成和MySQL类似,索引都是按照最左匹配规则去触发的,一条索引的中间部分跳过了就无效了。
  3. 参与sort的字段的排序方式,要和创建索引时的排序方式保持完全一致,或者完全相反。对于第一点里的索引,如果查询sort({"A":-1,"B":1})或者sort({"A":1,"B":-1}),索引则不会生效。只有在查询sort({"A":1,"B":1})或者sort({"A":-1,"B":-1})时,索引才会生效。

总结

  1. MongoDB的查询结果在进行排序时,如果排序字段没有添加索引,会将数据全部放到内存中计算。如果数据量过大,超过配置的内存大小,则会报错。
  2. 4.3版本之前,使用内存的最大值通过参数internalQueryExecMaxBlockingSortBytes控制,默认为32MB。4.3版本之后,通过参数internalQueryMaxBlockingSortMemoryUsageBytes控制。
  3. 正常的解决方式是添加索引,但是索引要包括全部参与排序的字段,且要遵循前缀匹配策略。

MongoDB排序时内存大小限制和创建索引的注意事项相关推荐

  1. Mongo聚合函数使用Sort排序时内存不足导致查询失败,开启allowDiskUse=true

    目录 1.问题描述 2.问题解决 1. Java代码方式解决办法 2. Mongo 原生聚合函数解决办法 1.问题描述 由于项目原因, 需要向数据存储至mongo,在使用过程中 , 单个documen ...

  2. mysql内存淘汰_从创建索引过程中内存变化来看SQL Server与MySQL的内存淘汰算法

    在sqlserver中,几年之前就注意到一个现象:sqlserver中对一个大表创建索引或者rebuild索引的过程中,会引起内存剧烈的动荡,究其原因为何,这种现象到底正不正常,是不是sqlserve ...

  3. java语言中 说明或声明数组时内存大小,说明或声明数组时不分配内存大小,创建数组时分配内存大小。...

    试井过程中每一实测的压力.说明产量都不随时间变化的叫(). 或声学制具体规定着() 明数义务教育的主要特点() 组时组<中华人民共和国义务教育法>是哪一年颁布的?() 现代社会的迅速变化, ...

  4. mysql 排序字段是否需要建索引_MySQL索引详解(优缺点,何时需要/不需要创建索引,索引及sql语句的优化)...

    一.什么是索引? 索引是对数据库表中的一列或多列值进行排序的一种结构,使用索引可以快速访问数据库表中的特定信息. 二.索引的作用? 索引相当于图书上的目录,可以根据目录上的页码快速找到所需的内容,提高 ...

  5. mongodb 排序_技术分享 | MongoDB 一次排序超过内存限制的排查

    本文目录: 一.背景 1. 配置参数检查 2. 排序字段是否存在索引 二.测试环境模拟索引对排序的影响 1. 测试环境信息 2. 报错语句的执行计划解释 3. 建立新的组合索引进行测试 三.引申的组合 ...

  6. Linux创建线程时 内存分配的那些事

    文章目录 问题描述 问题分析 针对问题1 的猜测: 针对问题2 的猜测: 原理追踪 总结 问题描述 事情开始于一段内存问题,通过gperf工具抓取进程运行过程中的内存占用情况. 分析结果时发现一个有趣 ...

  7. mongodb mysql资源占用_如何限制mongodb启动时占用过多内存

    默认情况下,mongodb占用的内存大小为:Starting in 3.4, the WiredTiger internal cache, by default, will use the large ...

  8. 开两个服务内存溢出_应用服务OkHttpClient创建大量对外连接时内存溢出

    1 背景 最近工作中碰到一个生产问题,就是应用服务在使用 OkHttpClient 时,在创建大量对外连接时线程堆积导致内存溢出. 主要表现是在流量极低的情况下,即平均 qps 在 1-4 左右的情况 ...

  9. Java运行jar包时,指定内存大小

    参考:Java运行时指定内存大小_u013738122的博客-CSDN博客 java -jar -Xms1024m -Xmx1536m -XX:PermSize=128M -XX:MaxPermSiz ...

最新文章

  1. Python学习笔记.OS学习笔记 OS操作系统(operating system)(一)
  2. 运营书籍:新媒体运营实战笔记
  3. redis在mac上的安装
  4. 如何删除git远程分支
  5. 物联网的四种计算类型
  6. 手机音频拼接软件_良心分享:业界最顶尖的软件!
  7. 中小企业大数据应用之道:思维在于借力
  8. c语言链表集合求并集用字母表示,c语言实现的链表集合的并集与交集
  9. java多线程系类:JUC原子类:04之AtomicReference原子类
  10. ARC097F Monochrome Cat
  11. mysql分页下表_有关Mysql分表分页问题
  12. windows 下 修改mysql配置文件
  13. VC6.0和VC2012的全局对象的释放!!!
  14. python批量将word转换成pdf_python批量实现Word文件转换为PDF文件
  15. C# 简单实现QQ截图功能
  16. 计算机专业买啥样笔记本,高考结束,想学计算机专业,买台啥样的笔记本比较好,暑假自学c和cpp...
  17. 使用智遥工作流,优化SAP请购流程
  18. OPPO开放平台移动应用认领
  19. 高通SDX12:基于sgm4151x的充电IC代码架构
  20. Zabbix报警机制 、 Zabbix进阶操作 、 监控案例

热门文章

  1. 分布式机器学习(上)-并行计算与机器学习
  2. QTP无法录制IE浏览器的web操作
  3. 2022-2027年中国流动餐饮车行业市场调研及未来发展趋势预测报告
  4. 左程云——归并排序和快速排序
  5. 在本地节点调试webhook
  6. CAD梦想画图中的“剪切命令”
  7. java+mysql基于SSM共享型汽车租赁系统
  8. 收集Oracle备份恢复信息
  9. EF CodeFirst下,当实体结构发生修改怎么更新数据库结构
  10. Linux中proc浅析