我有一个用Nodejs编写的系统,它首先要将非常大的csv文件中的记录导入数据库。使用Sequelize作为我的ORM,我创建了一个简单的模型如下:
"use strict";
const Sequelize = require('sequelize');
const sequelize = new Sequelize('mm', 'root', 'password', {
host: 'localhost',
dialect: 'mysql',
logging: true,
pool: {max: 5, min: 0, idle: 100000},
});
const Index = sequelize.define('index', {
value: {type: Sequelize.FLOAT}
});
然后我编写了以下代码来循环遍历文件中的行,解释这些行,并将它们写入数据库:
let readline = require('readline');
let moment = require('moment');
let lineReader = readline.createInterface({
input: require('fs').createReadStream('files/price_index.csv')
});
lineReader.on('line', function (line) {
let splitted = line.split(',');
let dt = moment(parseInt(splitted[0]));
let value = parseFloat(splitted[1]);
console.log(dt.format(), value);
Index.create({value: value, createdAt: dt});
});
这样可行,但每3120条记录后暂停约3秒钟。我尝试了sqlite和mysql,但它总是在3120条记录之后暂停。
看到Sequelize在3120条记录之后也开始记录插入查询,我认为这种行为的原因是某种缓存机制,它将所有查询放入队列,直到它要么无关,要么就是它点击魔法查询缓存限制,这正好是3120条记录。
我尝试在Sequelize的初始化中增加pool.max
数字,但这似乎没有任何区别。
任何人都可以确认我的缓存想法,或者解释一下这种行为的真正原因是什么?我可以以某种方式更改此行为,以便它具有一致的吞吐量?欢迎所有提示!
答案 0 :(得分:2)
我认为3120行将是the high water mark for the createReadStream
buffer which is 64KiB。当缓冲区已满时,节点将退回读取。
看起来3120 line
个事件都在同一个节点事件标记上运行,因此您可以处理3120行和3120个异步Index.create
调用以进行下一个滴答。所以你最终会在每一方都做大量的处理。读取和调度查询,或处理大量的计划查询。
当完成3120 line
个事件功能时,会发生一些垃圾收集,并且3120个已安排的create
个续订呼叫有机会完成他们的工作。这是"暂停"在数据中,但Node仍在处理中。所有create
次调用都需要几秒钟才能完成,然后再进行一些垃圾回收并返回到下一个csv数据块及其所有line
个事件。这个过程就像那样来回。
在一个包含10000行的csv文件中,我看到~3个查询能够在读取并计划插入所有10000行csv数据之前运行。
您可能希望使用较小块的Readable Stream。然后基于sequelize插入完成阻止读取。您可能需要对自己进行处理,而不是使用readline
。如果csv文件适合内存,只需阅读整个内容,因为调度将更容易。
也许使用类似queue
的内容来管理插入内容,允许最后一个续集池max
作为concurrency
。然后,一旦队列的length
足够低,允许读取再次发生。
我不知道最终结果是否会更快,但最终可能会非常相似。