我有一个大约 1000 万条记录的 mysql 表,我想使用 NodeJs 将这些记录发送到 csv 文件。
我知道我可以进行查询以获取所有记录,将结果存储在 json 格式变量中,然后使用 fastcsv 之类的库与 createWriteStream 一起将它们发送到 csv 文件。使用流将结果写入文件是可行的。但我要避免将 1000 万条记录存储到内存中(假设记录有很多列)。
我想做的是只查询结果的一个子集(例如 20k 行),将信息存储到文件中,然后查询下一个子集(下 20k 行)并将结果附加到同一个文件并继续处理直到完成。我现在遇到的问题是我不知道如何控制下一次迭代的执行。根据调试,由于 nodejs 的异步特性给我一个文件,其中一些行混合(同一行中有多个结果)和无序记录,因此同时执行了不同的写入操作。
我知道总执行时间会受到这种方法的影响,但在这种情况下,我更喜欢一种受控的方式并避免内存消耗。
对于数据库查询,我使用 MySQL 的 sequelize,但无论查询方法如何,想法都是相同的。
到目前为止,这是我的代码:
// Store file function receives:
// (String) filename
// (Boolean) headers: first iteration is true to put a name to the columns
// (json document) jsonData is the information to store in te file
// (Boolean) append: Disabled the first iteration to create a new file
const storeFile = (filename, headers, jsonData, append) => {
const flags = append === true ? 'a' : 'w'
const ws = fs.createWriteStream(filename, { flags, rowDelimiter: '\r\n' })
fastcsv
.write(jsonData, { headers })
.on('finish', () => {
logger.info(`file=${filename} created/updated sucessfully`)
})
.pipe(ws)
}
// main
let filename = 'test.csv'
let offset = 0
let append = false
let headers = true
const limit = 20000
const totalIterations = Math.ceil(10000000/ limit)
for (let i = 0; i < totalIterations; i += 1) {
// eslint-disable-next-line no-await-in-loop
const records = await Record.findAll({
offset,
limit,
raw: true,
})
storeFile(filename, headers, records, append)
headers = false
append = true
offset += limit // offset is incremented to get the next subset
}