当前位置: 首页 > 后端技术 > Node.js

如何高效遍历MongoDB超大集合?

时间:2023-04-04 00:05:02 Node.js

GitHub仓库:Fundebug/loop-mongodb-big-collection本文使用的编程语言是Node.js,连接MongoDB的模块使用的是mongoose。但是,本文介绍的方法适用于其他编程语言及其对应的MongoDB模块。错误的方法:find()或许,当遍历MongoDB集合时,我们会这样写:等待findAllMembers();让N=0;等待承诺。mapSeries(members,member=>{N++;console.log(`nameofthe${N}thmember:${member.name}`);});console.log(`loopall${N}memberssuccess`);}test();注意我们使用的是Bluebird的mapSeries而不是map,members数组中的元素是一个一个处理的。这够了吗?当Member集合中的文档不多的时候,比如只有1000个,那就真的没有问题了。但是当会员集合中有1000个文档时,会产生什么呢?如下:<---LastfewGCs--->rtofmarking1770ms)(averagemu=0.168,currentmu=0.025)finalize[5887:0x43127d0]33672ms:Mark-sweep1398.3(1425.2)->1398.0(1425.7)MB,1772.0/0.0ms(+0.1msin12stepssincethestartofmarking,maximumstep0.0ms,walltimesincethestartofmarking1775ms)(平均mu=0.088,当前mu=0.002)最终确定[5887:0x43127d0]35172ms:Mark-sweep1398.5(1425.7)->1398.4(1428.7)MB,1496.7/0.0ms(平均mu=0.049,当前mu=0.002)分配失败清除可能不会成功<---JSstacktrace--->致命错误:接近堆限制的无效标记压缩分配失败-JavaScript堆内存不足1:0x8c02c0node::Abort()[node]2:0x8c030c[节点]3:0xad15dev8::Utils::ReportOOMFailure(v8::internal::Isolate*,charconst*,bool)[节点]4:0xad1814v8::internal::V8::FatalProcessOutOfMemory(v8::internal::隔离*,charconst*,bool)[node]5:0xebe752[node]6:0xebe858v8::internal::Heap::CheckIneffectiveMarkCompact(unsignedlong,double)[node]7:0xeca982v8::internal::Heap::PerformGarbageCollection(v8::内部::垃圾收集器,v8::GCCallbackFlags)[节点]8:0xecb2b4v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace,v8::internal::GarbageCollectionReason,v8::GCCallbackFlags)[节点]9:0xecba8av8::internal::Heap::FinalizeIncrementalMarkingIfComplete(v8::internal::GarbageCollectionReason)[node]10:0xecf1b7v8::internal::IncrementalMarkingJob::Task::RunInternal()[node]11:0xbc1796v8::internal::CancelableTask::Run()[node]12:0x935018node::PerIsolatePlatformData::FlushForegroundTasksInternal()[node]13:0x9fccff[node]14:0xa0dbd8[node]15:0x9fd63buv_run[node]16:0x8ca6c5node::Start(v8::Isolate*,node::IsolateData*,int,charconst*const*,int,charconst*const*)[node]17:0x8c945fnode::Start(int,char**)[节点]18:0x7f84b6263f45__libc_start_main[/lib/x86_64-linux-gnu/libc.so.6]19:0x885c55[node]Aborted(coredumped),可见内存不足。打印find()返回的成员数组。可以看出集合中的所有元素都已经返回了,哪个数组可以容纳1000万个对象?正确的方法:find().cursor()和eachAsync()将返回整个集合find()。应该避免这种操作。正确的方法应该是这样的:让N=0;awaitmembersCursor.eachAsync(member=>{N++;console.log(`nameofthe${N}thmember:${member.name}`);});console.log(`loopall${N}memberssuccess`);}test();使用cursor()方法返回QueryCursor,然后使用eachAsync()遍历整个集合,不用担心内存不足。什么是查询游标?查看mongoose文档:QueryCursor是一种并发原语,用于一次处理一个文档的查询结果。QueryCursor实现了Node.jsstreams3API,此外还有其他几种从MongoDB一次加载文档的机制。总之,QueryCursor一次可以从MongoDB中取出一个文档,这显然大大减少了内存占用。如何测试?这篇博客的内容很简单,但也很容易被人忽视。如果你测试它,你会印象更深刻。测试代码很简单,可以查看Fundebug/loop-mongodb-big-collection。我的测试环境是这样的:ubuntu14.04mongodb3.2nodejs10.9.01。使用Docker运行MongoDBsudodockerrun--net=host-d--namemongodbdaocloud.io/library/mongo:3.22。使用mgodatagen使用mgodatagen生成测试数据,1分钟多可以生成1000万个文档!下载mgodatagen:https://github.com/feliixx/mgodatagen/releases/download/0.7.3/mgodatagen_linux_x86_64.tar.gz解压后复制到/usr/local/bin目录下:sudomvmgodatagen/usr/local/binmgodatagen的配置文件mgodatagen-config.json如下:[{"database":"test","collection":"members","count":10000000,"content":{"name":{"类型”:“字符串”,“最小长度”:2,“最大长度”:8},“城市”:{“类型”:“字符串”,“最小长度”:2,“最大长度”:8},“国家”:{“类型”:“字符串”,“最小长度”:2,“最大长度”:8},“公司”:{“type":"string","minLength":2,"maxLength":8},"email":{"type":"string","minLength":2,"maxLength":8}}}]执行mgodatagen-fmgodatagen-config.json命令生成1亿测试数据mgodatagen-fmgodatagen-config.json连接mongodb://127.0.0.1:27017MongoDBserverversion3.2.13collectionmembers:done[=====================================================================]100%+------------+--------+-----------------+----------------+|集合|计数|平均对象大小|索引|+------------+-----------+----------------+----------------+|成员|10000000|108|_id_95368kB|+------------+------------+----------------+---------------+runfinishedin1m12.82s查看MongoDB,可以看到新生成的数据是0.69GB,其实很小,但是使用find()方法遍历会报错showdbslocal0.000GBtest0.690GB3.执行测试代码两种不同遍历方法的代码分别位于test1.js和test2.js中。参考如何使用mongoose遍历100万+的mongodb表CursorsinMongoose4.5关于FundebugFundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、ReactNative、Node.js和Java在线应用的实时BUG监控.自2016年双十一正式上线以来,Fundebug累计处理了10亿+错误事件,其付费客户包括谷歌、360、金山、人民网等众多品牌公司。欢迎大家免费试用!转载版权声明请注明作者Fundebug及本文地址:https://blog.fundebug.com/2019/03/21/how-to-visit-all-documents-in-a-big-collection-of-mongodb/