【问题标题】:How to load very large csv files in nodejs?如何在nodejs中加载非常大的csv文件?
【发布时间】:2018-11-01 07:41:58
【问题描述】:

我正在尝试将 2 个大 csv 加载到 nodejs 中,第一个的大小为 257 597 ko,第二个的大小为 104 330 ko。我正在使用文件系统 (fs) 和 csv 模块,这是我的代码:

fs.readFile('path/to/my/file.csv', (err, data) => {
  if (err) console.err(err)
  else {
    csv.parse(data, (err, dataParsed) => {
      if (err) console.err(err)
      else {
        myData = dataParsed
        console.log('csv loaded')
      }
    })
  }
})

经过一段时间(1-2 小时)后,它就会崩溃并显示以下错误消息:

<--- Last few GCs --->

[1472:0000000000466170]  4366473 ms: Mark-sweep 3935.2 (4007.3) -> 3935.2 (4007.
3) MB, 5584.4 / 0.0 ms  last resort GC in old space requested
[1472:0000000000466170]  4371668 ms: Mark-sweep 3935.2 (4007.3) -> 3935.2 (4007.
3) MB, 5194.3 / 0.0 ms  last resort GC in old space requested


<--- JS stacktrace --->

==== JS stack trace =========================================

Security context: 000002BDF12254D9 <JSObject>
    1: stringSlice(aka stringSlice) [buffer.js:590] [bytecode=000000810336DC91 o
ffset=94](this=000003512FC822D1 <undefined>,buf=0000007C81D768B9 <Uint8Array map
 = 00000352A16C4D01>,encoding=000002BDF1235F21 <String[4]: utf8>,start=0,end=263
778854)
    2: toString [buffer.js:664] [bytecode=000000810336D8D9 offset=148](this=0000
007C81D768B9 <Uint8Array map = 00000352A16C4D01>,encoding=000002BDF1...

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memo
ry
 1: node::DecodeWrite
 2: node_module_register
 3: v8::internal::FatalProcessOutOfMemory
 4: v8::internal::FatalProcessOutOfMemory
 5: v8::internal::Factory::NewRawTwoByteString
 6: v8::internal::Factory::NewStringFromUtf8
 7: v8::String::NewFromUtf8
 8: std::vector<v8::CpuProfileDeoptFrame,std::allocator<v8::CpuProfileDeoptFrame
> >::vector<v8::CpuProfileDeoptFrame,std::allocator<v8::CpuProfileDeoptFrame> >
 9: v8::internal::wasm::SignatureMap::Find
10: v8::internal::Builtins::CallableFor
11: v8::internal::Builtins::CallableFor
12: v8::internal::Builtins::CallableFor
13: 00000081634043C1

加载了最大的文件,但节点耗尽了另一个文件的内存。分配更多内存可能很容易,但这里的主要问题是加载时间,尽管文件很大,但它似乎很长。那么正确的做法是什么? Python 使用 pandas 加载这些 csv 的速度非常快(3-5 秒)。

【问题讨论】:

    标签: node.js csv


    【解决方案1】:

    Stream 完美运行,仅需 3-5 秒:

    var csv = require('csv-parser')
    var data = []
    
    fs.createReadStream('path/to/my/data.csv')
      .pipe(csv())
      .on('data', function (row) {
        data.push(row)
      })
      .on('end', function () {
        console.log('Data loaded')
      })
    

    【讨论】:

    • 读取流也中断了。
    • 我认为,这里的数据数组将存储每个文件,最终它将整个文件保存在一个变量中。而不是该用户可以直接使用该数据执行某些任务,例如:数据库操作。
    【解决方案2】:

    fs.readFile 会将整个文件加载到内存中,但 fs.createReadStream 会以您指定大小的块读取文件。

    这将防止它耗尽内存

    【讨论】:

      【解决方案3】:

      您可能希望流式传输 CSV,而不是一次读取所有内容:

      【讨论】:

      • 当心我曾经尝试使用csv-parse,但我无法限制readable 事件;解析器读取速度非常快,我不得不为其分配大量 RAM。对于像 1GB 这样的 CSV 文件可能会很棘手......如果我不得不重试,我会搜索类似 Promise 的库或能够处理承诺/回调。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-08
      • 1970-01-01
      相关资源
      最近更新 更多