node.js的内存使用量在做500k个mongodb插入时爆炸

我正在将数据从MySQL迁移到MongoDB。

MySQL根本没有优化(没有索引),所以我的操作,1个普通的sql查询和1个sql的sequelize查询每个约30秒,每个返回约300k个文档。

我不担心MySQL部分,但是关于节点/ mongo部分。

当他们插入到MongoDB,我已经尝试了几件事情:

  • 使用本地驱动程序和数组插入,我没有看到任何问题,它是快速的。 我只是将查询结果聚合成几个中等大小的数组(<16MB)并插入它们。 但我想做upserts,而不是插入不破坏修改后的数据,并不支持批量操作。
  • 使用mongoose和单一的upserts,节点进程缓慢地走向我给它的mem限制(3GB),然后似乎停在那里。 30分钟后,没有什么明显的进展,mongodb进程处于0%的CPU。 (我认为,如果它有足够的内存用于给定数量的数据,它通常会在很久以后完成。)
  • 使用本地驱动程序和单个upserts,整个过程运行非常缓慢,但至less完成。 看到任务pipe理器(Windows系统)中的CPU使用情况,我可以看到,MySQL正在使用CPU,当它应该(当我开始2个查询,第二个第一个完成后,所有的结果已经插入到mongodb (至less从节点一方,因为他们到现在还没有到达)),并且Mongod在开始时显示CPU负载很短,但是它保持在0%,而node.js抓取越来越多的内存。 只是在很久之后,mongod似乎再次启动,并且在某个时刻,mongod也回到了0%的CPU时间点,节点的内存使用率反转并恢复正常。 奇怪的是,到那个时候,我所有的查询/插入都是很早以前完成的,而实际的工作(从CPU使用情况以及从mongodb中的实际数据中可以看出,预期的对象)只是在后来发生巨大的内存和CPU负载(在大多数操作期间,CPU负载节点进程最大1个核心)。

那么,上面的第三种情况对我来说是最有趣的,因为它正在工作,给我我想要的东西,它只是performance不太理解。

为什么这个“真正”的工作只是在我叫更新之后才做的?

为了加快这个过程,我也消除了写关注/callback,似乎有所帮助。

典型的upsert看起来像这样:

connectionb.update({ isbn13: book.isbn13 }, { $addToSet: { ratedByUsers: insertuserrating } }, { w: 0 }); 

  connectionu.update(insertuser, { $addToSet: { ratedBooks: insertbookrating } }, { upsert: true, w: 0 }); 

此外,我不认为我有内存泄漏,因为总体而言,后来我发现内存再次回落到几个100MB。 (不在这里 – 我把这个属性归咎于GC懒惰,但是我反复运行这个操作,并且看到内存再次大幅下降。)


我试图更好地说明行为:你可以看到MySQL CPU消耗中的2个峰值,这是我做2次查询所期望的。

MongoD耸人听闻。

MongoD和MySQL一直使用很less的内存。

节点几乎总是运行全1-CPU,内存首先快速增加,然后缓慢下降。

CPU和内存消耗

我期望node.js能够做更less的工作,并且mongod会一直忙于尝试插入数据。 另外,我对这个时间感到惊讶:节点表示在大约2分钟后(所有代码都运行完毕),所有操作都完成了 – 但是在那个时候,MongoDB中几乎没有任何数据。

难道我做错了什么?

如何减less节点内存和CPU消耗,加快数据到达mongodb的时间?


增加了真实的代码

(删除了很​​多,像我认为不相关的模型和查询)。 connectionu和connectionb是与用户和书籍集合的mongodb连接。

首先,所有的图书都是导入的,然后所有的用户都是导入的 – 由于mysql数据库结构,我使用的评级决定了我感兴趣的用户,并且还用收视率再次更新图书collections。

  sequelize.query(qstring).success(function (result) { var len = result.length; result.forEach(function ProcessRatingResult(oneresult) { var insertobject = { isbn13: oneresult.isbn13, title: oneresult.title, }; cntwrite++; connectionb.update({ isbn13: oneresult.isbn13 }, insertobject, { upsert: true, w: 0 }); // only start 2nd part if first part is completely finished - 135s runtime if (cntwrite == (len - 1)) { // start with slight delay - give mongo/node time to catch up setTimeout(ImportRating, 10000); //2500); } }); }); function ImportRating() { SQLRating.findAll({ where: {}, include: [SQLUser, SQLBook] }).success(function (sqlresult) { async.each(sqlresult, function ProcessRatingResult(oneresult) { var book = oneresult.book; var user = oneresult.user; var rating = oneresult.rating; (function (book, user, rating) { var insertuser = { joined: user.created, external_id: user.external_id }; var insertbookrating = { book: book.isbn13, rating: rating } connectionu.update(insertuser, { $addToSet: { ratedBooks: insertbookrating } }, { upsert: true, w: 0 }); var insertuserrating = { user: user.external_id, rating: rating } connectionb.update({ isbn13: book.isbn13 }, { $addToSet: { ratedByUsers: insertuserrating } }, { w: 0 }); })(book, user, rating); } , function (err) { throw err; } ); }); }