Javascript 使用带有Node.js的流写入和读取大型数组

Javascript 使用带有Node.js的流写入和读取大型数组,javascript,arrays,node.js,typescript,stream,Javascript,Arrays,Node.js,Typescript,Stream,我有一个巨大的物体,它就像一张有270万个钥匙的地图。我尝试将对象写入文件系统,以便持久化它,而不是每次需要时重新计算它。在另一个步骤中,我需要再次读取对象。我需要访问内存中的整个对象,因为它需要用作地图 为了便于编写,我将对象转换为数组,并使用下面的函数将其流式传输到文件系统。我首先将其转换为数组的原因是,流式处理数组而不是对象似乎要快得多。写作部分大约需要一分钟,这很好。输出文件的大小为4,8GB 我面临的问题是在尝试读取文件时。为此,我创建一个读取流并解析内容。 然而,出于某种原因,我似乎

我有一个巨大的物体,它就像一张有270万个钥匙的地图。我尝试将对象写入文件系统,以便持久化它,而不是每次需要时重新计算它。在另一个步骤中,我需要再次读取对象。我需要访问内存中的整个对象,因为它需要用作地图
为了便于编写,我将对象转换为数组,并使用下面的函数将其流式传输到文件系统。我首先将其转换为数组的原因是,流式处理数组而不是对象似乎要快得多。写作部分大约需要一分钟,这很好。输出文件的大小为4,8GB
我面临的问题是在尝试读取文件时。为此,我创建一个读取流并解析内容。 然而,出于某种原因,我似乎达到了某种记忆极限。我使用了各种不同的方法来读取和解析数据,直到读取了大约50%的数据为止(此时,我机器上的节点进程占用6GB内存,略低于我设置的限制)。从那时起,读取时间显著增加了10倍,可能是因为节点接近使用最大分配内存限制(6144MB)。感觉好像我做错了什么
我不明白的主要问题是为什么写入不是问题,而读取是,即使在写入步骤中,整个数组也保存在内存中。我正在使用node
v8.11.3

因此,总结一下:

  • 我有一个大对象,需要使用streams作为数组持久化到文件系统
  • 写作很好
  • 直到大约50%的数据被读取,读取时间才会显著增加
我怎样才能更有效地读取文件

我尝试了各种图书馆,比如,

要写入的对象的示例:

{'id':['some_other_id_1','some_other_id_2']
然后在写入之前将其转换为数组:

[{'id':['some_other_id_1','some_other_id_2']]
函数使用流将数组写入文件系统:

import*作为fs从“fs”导入
从“jsonStream”导入*作为jsonStream
从“stream array”导入*作为streamifyArray
异步函数writeFileStreamFromArray(pathToFile:string,fileContent:any[]):承诺{
返回新承诺((解决、拒绝)=>{
const fileWriterStream=fs.createWriteStream(路径文件)
const stringifierStream=jsonStream.stringify()
const readStream=streamifyArray(fileContent)
readStream.pipe(stringifierStream)
stringifierStream.pipe(fileWriterStream)
fileWriterStream.on('finish',()=>{
console.log('writeFileStreamFromArray:文件已写入')
stringifierStream.end()
解决()
})
fileWriterStream.on('error',(err)=>{
console.log('err',err)
拒绝(错误)
})
})
}
使用jsonStream从流中获取数组的函数:

异步函数getArrayFromStreamUsingJsonStream(路径文件:字符串):Promise{ 返回新承诺(异步(解析、拒绝)=>{ const readStream=fs.createReadStream(路径文件) const parseStream=jsonStream.parse(“*”) 常量数组=[] const start=Date.now() const transformer=变换((条目)=>{ array.push(条目) 如果((array.length%100000)==0){ const end=(Date.now()-start)/1000 console.log('array',array.length,end) } }) readStream.pipe(parseStream) parseStream.pipe(变压器) readStream.on('end',()=>{ log('getArrayFromStreamUsingJsonStream:array created') parseStream.end() 解析(数组) }) readStream.on('error',(error)=>{ 拒绝(错误) }) }) } 计时日志(在1200000个条目之后,我取消了执行,因为它花费了很长时间):

阵列100000 6.345
阵列200000 12.863
阵列300000 21.177
阵列400000 29.638
阵列500000 35.884
阵列600000 42.079
阵列700000 48.74
阵列800000 65.662
阵列900000 89.805
数组1000000 120.416
阵列1100000 148.892
阵列1200000 181.921
...
预期结果:应该比目前更具性能。 这可能吗?还是我遗漏了一些明显的东西


非常感谢您的帮助

我怀疑它的内存不足,因为您试图将所有条目读取到一个连续数组中。当阵列填满时,节点将重新分配阵列并将现有数据复制到新阵列。因此,随着阵列越来越大,速度也越来越慢。因为它在重新分配时必须有两个阵列,所以它也将使用比阵列本身更多的内存

您可以使用一个数据库,因为数百万行不应该是问题,或者编写您自己的读/写例程,确保使用允许非顺序块内存分配的内容,例如


例如,预先分配一个10k长的数组条目,将映射的前10k条目读入数组并将数组写入文件。然后将接下来的10k条目读入数组,并将其写入新文件。重复此操作,直到处理完所有条目。这将减少内存使用,并有助于通过并行运行IO来加快速度,而不必使用更多内存。

谢谢您的回答!你说的对我有意义。我想使用数据库是最好的选择!你对使用哪种数据库有什么建议吗?我不确定哪一个符合我的标准(在几分钟内写和读数百万条)@jaga如果你还没有数据库,那么我会尝试MySQL或Postgres,因为两者都可以完成这项工作。性能取决于硬件,而不是DB的选择。在你走这条路之前,想想其他存储地图的方法。我更新了我的答案,提出了一种解决方法。再次感谢您的支持