【问题标题】:save many records to couchdb in nodejs在nodejs中将许多记录保存到couchdb
【发布时间】:2014-07-06 06:21:37
【问题描述】:

我有一个very large dataset,我想将它保存在 couchdb 中以供搜索。

我希望记录看起来像这样:

{
  "type": "first",
  "name": "ryan",
  "count": 447980
}

由于文本文件比我应该在内存中保存的要大,我正在设置一个流式 readline 阅读器,如下所示:

var db = require('./db'),
    readline = require('readline'),
    path = require('path'),
    fs = require('fs');

// simple callback after cradle save
function saveHandler(er, doc){
    if (er) return console.log('Error: ', er);
    console.log(doc);
}

// save record of type, based on line with count & name
function handleCountedLine(type, line){
    return function(line){
        var record = {type:type};
        var i = line.trim().split(' ');
        record.name = i[1].trim();
        record.count = Number(i[0]);
        db.save(record, saveHandler);
    }
}

var handleFirst = handleCountedLine('first');
readline.createInterface({
    input: fs.createReadStream('data/facebook-firstnames-withcount.txt'),
    terminal: false
})
.on('line', handleFirst);

db 是一个摇篮 db。

大约 40 条记录后,它会减慢到完全爬行,然后最终耗尽内存。我尝试了poolrnode-rate-limiter,使用“一次只运行这么多”和“只允许在一分钟内运行这么多”策略。两者都工作得更好,但它仍然内存不足。有没有实现这个目标的好方法,还是我卡在writing it in python

【问题讨论】:

  • 谷歌环聊中的 Paulo Machado 提到我应该暂停并恢复油门,我不知道我可以这样做。我做了这个,它在停止之前处理了 60 条记录(更好但仍然没有解决):gist.github.com/konsumer/6d4003a6ff49c78c8a59
  • 我不会使用 readline。它会让你慢下来。
  • 我是在没有 readline 的情况下完成的(使用自定义流),它有点慢。

标签: node.js couchdb


【解决方案1】:

在 Paulo Machado 在 Google Hangouts 中的出色帮助下,我使用 line-by-line 做出了回答,这是一个使用 stream.pause() 和 stream.resume() 的简单包装器,一次只允许处理一行.我想表扬他,但他还没有来这里回答,所以我就把这个放在这里。到目前为止,它已经解析了 34039 条记录。如果它崩溃了,我会更新答案。

var LineByLineReader = require('line-by-line'),
  path = require('path'),
  db = require('./db')

// line-by-line read file, turn into a couch record
function processFile(type){
  var fname = path.join('data', types[type] + '.txt');
  var lr = new LineByLineReader(fname, {skipEmptyLines: true});

  lr.on('error', function (err) {
    console.log('Error:');
    console.log(err);
  });

  lr.on('record', function (record) {
    console.log('Saved:');
    console.log(record);
  });

  lr.on('line', function (line) {
    lr.pause();
    var record = { type: type };

    if (type == 'full'){
      record.name = line.trim().split(' ');
    }else{
      var i = line.trim().split(' ');
      record.name = i[1].trim();
      record.count = Number(i[0]);
    }

    db.save(record, function(er, res){
      if (er) lr.emit('error', er, record);
      if (res) lr.emit('record', record);
      lr.resume();
    })
  });
}

var types = {
  'first':'facebook-firstnames-withcount',
  'last':'facebook-lastnames-withcount',
  'full':'facebook-names-unique'
};

for (type in types){
  processFile(type);
}

// views for looking things up
db.save('_design/views', require('./views'));

【讨论】:

  • 仍有大约 95,000 条记录!
【解决方案2】:

我猜 couchdb 是这里的瓶颈。看看couchdb's bulk doc api,它允许您插入文档。 (您可能不应该尝试一次提交所有数据,而是在一个数组中累积一堆文档并将其推送到数据库 - 使用 stream.pause() 和 stream.resume() 来限制文本流)。如果您使用批量 api,您将获得efficiency gains by couchdb 奖励。

【讨论】:

  • 使用批量文档 API,它也会耗尽内存(它需要您加载要更新的记录数组。)
  • 正如您(或者更确切地说是 Paulo Machado)在上面的评论中提到的,您仍然需要限制流。编辑了我的答案以反映这一点。
  • 我听到你在说什么,但我的意思是解决问题的不是批量 API,而是逐行的,它暂停了读取流(不仅仅是readline 流,我也尝试过。)由于我的替代方案只是在 python 中创建一个阻塞脚本,我并不关心使用间歇性数组和批量 API 的性能提升,特别是考虑到在上面的示例中,它仍然是等效 python 的 3 倍左右(有 3 个并发流,每个文件 1 个。)
  • 管理临时数组和检查队列状态所需的额外代码对我来说实际上不如为记录调用多个 HTTP 请求的开销那么可取。如果我需要在流中注入其他的东西,这也适用于更加模块化的模式。
  • 您可能会考虑使用多个节点进程来抽象您的模块化要求,将文件名和类型传递给您的脚本。但是请注意,这些要求不是您最初问题的一部分。另一方面,如果您关心速度,则可以使用批量文档 API(couchdb definitve guide 声称批量插入速度约为 2,700 文档/秒;如果多个进程的速度超过 3,600 文档/秒)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多