javascript - JavaScript堆内存不足-插入mongodb时出错
问题描述
我想在 MongoDB 中插入 1500000 个文档。首先,我查询一个数据库并从那里获取 15000 名教师的列表,我想为每个教师插入 100 门课程。
我运行了两个循环:首先它遍历所有讲师,其次,在每次迭代中,它将为该 ID 插入 100 个文档,如下面的代码所示:
const instructors = await Instructor.find();
//const insrtuctor contains 15000 instructor
instructors.forEach((insructor) => {
for(let i=0; i<=10; i++) {
const course = new Course({
title: faker.lorem.sentence(),
description: faker.lorem.paragraph(),
author: insructor._id,
prise: Math.floor(Math.random()*11),
isPublished: 'true',
tags: ["java", "Nodejs", "javascript"]
});
course.save().then(result => {
console.log(result._id);
Instructor.findByIdAndUpdate(insructor._id, { $push: { courses: course._id } })
.then(insructor => {
console.log(`Instructor Id : ${insructor._id} add Course : ${i} `);
}).catch(err => next(err));
console.log(`Instructor id: ${ insructor._id } add Course: ${i}`)
}).catch(err => console.log(err));
}
});
这是我的package.json
文件,我在其中放置了我在互联网上找到的内容:
{
"scripts": {
"start": "nodemon app.js",
"fix-memory-limit": "cross-env LIMIT=2048 increase-memory-limit"
},
"devDependencies": {
"cross-env": "^5.2.0",
"faker": "^4.1.0",
"increase-memory-limit": "^1.0.6",
}
}
这是我的课程模型定义
const mongoose = require('mongoose');
const Course = mongoose.model('courses', new mongoose.Schema({
title: {
type: String,
required: true,
minlength: 3
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'instructor'
},
description: {
type: String,
required: true,
minlength: 5
},
ratings: [{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'users',
required: true,
unique: true
},
rating: {
type: Number,
required: true,
min: 0,
max: 5
},
description: {
type: String,
required: true,
minlength: 5
}
}],
tags: [String],
rating: {
type: Number,
min: 0,
default: 0
},
ratedBy: {
type: Number,
min: 0,
default: 0
},
prise: {
type: Number,
required: function() { this.isPublished },
min: 0
},
isPublished: {
type: Boolean,
default: false
}
}));
module.exports = Course;
解决方案
对于大量数据,您必须使用 cursors。
想法是尽快处理文档,因为您从 db获得一个。
就像您要求 db 给讲师,而db以小批量发回,您使用该批次进行操作并处理它们,直到所有批次结束。
否则 await Instructor.find()
会将所有数据加载到内存并使用您不需要的 mongoose 方法填充该实例。
甚至await Instructor.find().lean()
不会给内存带来好处。
当您在收集时,光标是mongodb 的功能。find
使用猫鼬它可以使用:Instructor.collection.find({})
观看此视频。
下面我编写了使用游标批量处理数据的解决方案。
在模块内的某处添加:
const createCourseForInstructor = (instructor) => {
const data = {
title: faker.lorem.sentence(),
description: faker.lorem.paragraph(),
author: instructor._id,
prise: Math.floor(Math.random()*11), // typo: "prise", must be: "price"
isPublished: 'true',
tags: ["java", "Nodejs", "javascript"]
};
return Course.create(data);
}
const assignCourseToInstructor = (course, instructor) => {
const where = {_id: instructor._id};
const operation = {$push: {courses: course._id}};
return Instructor.collection.updateOne(where, operation, {upsert: false});
}
const processInstructor = async (instructor) => {
let courseIds = [];
for(let i = 0; i < 100; i++) {
try {
const course = await createCourseForInstructor(instructor)
await assignCourseToInstructor(course, instructor);
courseIds.push(course._id);
}
catch (error) {
console.error(error.message);
}
}
console.log(
'Created ', courseIds.length, 'courses for',
'Instructor:', instructor._id,
'Course ids:', courseIds
);
};
并在您的异步块中将您的循环替换为:
const cursor = await Instructor.collection.find({}).batchSize(1000);
while(await cursor.hasNext()) {
const instructor = await cursor.next();
await processInstructor(instructor);
}
PS我使用本机collection.find
和性能collection.updateOne
来避免猫鼬对模型实例上的猫鼬方法和字段使用额外的堆。
奖金:
即使使用 此游标解决方案您的代码将再次出现内存不足问题,请像本示例中那样运行您的代码(根据服务器的 ram 定义大小(以兆字节为单位)):
nodemon --expose-gc --max_old_space_size=10240 app.js
推荐阅读
- r - 如何在 a4 pdf 上放置多个 ggplot 图表?
- python-3.x - 如何直接在 tkinter 画布上捕获击键?
- javascript - 如何在 python 的字典中存储 lambda 函数?
- sql-server - 安装 SQL Server 2019 CU2 后,SQL Server 代理停止工作
- windows - 在 golang 中运行 windows 命令
- java - 如何使elasticsearch解析相同类型的json对象列表?
- php - PHP MySQL 计数总问题
- flutter - Flutter - 如何更改子小部件语言
- flutter - 在 Flutter 中的 FAB 上方显示 SnackBar / Toast
- c# - Podio C# 第一次使用 API 无法使用密码进行身份验证