MongoDB排序时内存大小限制和创建索引的注意事项
线上服务的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的问题,有两个方法:
- 给排序的字段加索引。具体怎么加索引,会在后面细讲。
- 修改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的索引交集也可以让这两个单键索引实现和复合索引一样的效果。
哦,不行哟,还是那个报错。
所以,当多字段排序时,你必须要建一个包含了这些字段的复合索引,且要注意以下几点:
- 查询时参与排序的多个字段的顺序,要和创建的索引每个字段的顺序保持一致。比如你创建的索引是:
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})
,则索引不会生效。 - 参与查询的字段少于索引的字段,则要保证符合前缀匹配。还是第一点里的索引,如果排序语句是这样:
sort({"A":1,"B":1})
,则索引继续生效。如果是这样:sort({"A":1,"C":1})
,则无法生效。这个你可以理解成和MySQL类似,索引都是按照最左匹配规则去触发的,一条索引的中间部分跳过了就无效了。 - 参与sort的字段的排序方式,要和创建索引时的排序方式保持完全一致,或者完全相反。对于第一点里的索引,如果查询
sort({"A":-1,"B":1})
或者sort({"A":1,"B":-1})
,索引则不会生效。只有在查询sort({"A":1,"B":1})
或者sort({"A":-1,"B":-1})
时,索引才会生效。
总结
- MongoDB的查询结果在进行排序时,如果排序字段没有添加索引,会将数据全部放到内存中计算。如果数据量过大,超过配置的内存大小,则会报错。
- 4.3版本之前,使用内存的最大值通过参数internalQueryExecMaxBlockingSortBytes控制,默认为32MB。4.3版本之后,通过参数internalQueryMaxBlockingSortMemoryUsageBytes控制。
- 正常的解决方式是添加索引,但是索引要包括全部参与排序的字段,且要遵循前缀匹配策略。
MongoDB排序时内存大小限制和创建索引的注意事项相关推荐
- Mongo聚合函数使用Sort排序时内存不足导致查询失败,开启allowDiskUse=true
目录 1.问题描述 2.问题解决 1. Java代码方式解决办法 2. Mongo 原生聚合函数解决办法 1.问题描述 由于项目原因, 需要向数据存储至mongo,在使用过程中 , 单个documen ...
- mysql内存淘汰_从创建索引过程中内存变化来看SQL Server与MySQL的内存淘汰算法
在sqlserver中,几年之前就注意到一个现象:sqlserver中对一个大表创建索引或者rebuild索引的过程中,会引起内存剧烈的动荡,究其原因为何,这种现象到底正不正常,是不是sqlserve ...
- java语言中 说明或声明数组时内存大小,说明或声明数组时不分配内存大小,创建数组时分配内存大小。...
试井过程中每一实测的压力.说明产量都不随时间变化的叫(). 或声学制具体规定着() 明数义务教育的主要特点() 组时组<中华人民共和国义务教育法>是哪一年颁布的?() 现代社会的迅速变化, ...
- mysql 排序字段是否需要建索引_MySQL索引详解(优缺点,何时需要/不需要创建索引,索引及sql语句的优化)...
一.什么是索引? 索引是对数据库表中的一列或多列值进行排序的一种结构,使用索引可以快速访问数据库表中的特定信息. 二.索引的作用? 索引相当于图书上的目录,可以根据目录上的页码快速找到所需的内容,提高 ...
- mongodb 排序_技术分享 | MongoDB 一次排序超过内存限制的排查
本文目录: 一.背景 1. 配置参数检查 2. 排序字段是否存在索引 二.测试环境模拟索引对排序的影响 1. 测试环境信息 2. 报错语句的执行计划解释 3. 建立新的组合索引进行测试 三.引申的组合 ...
- Linux创建线程时 内存分配的那些事
文章目录 问题描述 问题分析 针对问题1 的猜测: 针对问题2 的猜测: 原理追踪 总结 问题描述 事情开始于一段内存问题,通过gperf工具抓取进程运行过程中的内存占用情况. 分析结果时发现一个有趣 ...
- mongodb mysql资源占用_如何限制mongodb启动时占用过多内存
默认情况下,mongodb占用的内存大小为:Starting in 3.4, the WiredTiger internal cache, by default, will use the large ...
- 开两个服务内存溢出_应用服务OkHttpClient创建大量对外连接时内存溢出
1 背景 最近工作中碰到一个生产问题,就是应用服务在使用 OkHttpClient 时,在创建大量对外连接时线程堆积导致内存溢出. 主要表现是在流量极低的情况下,即平均 qps 在 1-4 左右的情况 ...
- Java运行jar包时,指定内存大小
参考:Java运行时指定内存大小_u013738122的博客-CSDN博客 java -jar -Xms1024m -Xmx1536m -XX:PermSize=128M -XX:MaxPermSiz ...
最新文章
- Python学习笔记.OS学习笔记 OS操作系统(operating system)(一)
- 运营书籍:新媒体运营实战笔记
- redis在mac上的安装
- 如何删除git远程分支
- 物联网的四种计算类型
- 手机音频拼接软件_良心分享:业界最顶尖的软件!
- 中小企业大数据应用之道:思维在于借力
- c语言链表集合求并集用字母表示,c语言实现的链表集合的并集与交集
- java多线程系类:JUC原子类:04之AtomicReference原子类
- ARC097F Monochrome Cat
- mysql分页下表_有关Mysql分表分页问题
- windows 下 修改mysql配置文件
- VC6.0和VC2012的全局对象的释放!!!
- python批量将word转换成pdf_python批量实现Word文件转换为PDF文件
- C# 简单实现QQ截图功能
- 计算机专业买啥样笔记本,高考结束,想学计算机专业,买台啥样的笔记本比较好,暑假自学c和cpp...
- 使用智遥工作流,优化SAP请购流程
- OPPO开放平台移动应用认领
- 高通SDX12:基于sgm4151x的充电IC代码架构
- Zabbix报警机制 、 Zabbix进阶操作 、 监控案例