使用Node.js将大对象数据异步写入Redis

时间:2015-04-11 19:40:32

标签: javascript node.js redis

我创建了一个Node.js脚本,它创建了大量随机生成的测试数据,我想将它写入Redis DB。我正在使用redis客户端库和async库。最初,我尝试在生成测试数据的redisClient.hset(...)循环中执行for命令,但在一些谷歌搜索之后,我了解到Redis方法是异步的,而for循环是同步的。在看到StackOverflow上的一些问题后,我无法按照我想要的方式工作。

我可以写一个Redis,而不会出现小数组或更大的数组,例如一个包含100,000个项目的数组。但是,当我有5,000,000个项目的数组时,它不能正常工作。我最终没有足够的内存,因为redis命令似乎正在排队,但是直到async.each(...)完成并且节点进程没有退出之后才执行。如何调用redisClient.hset(...)

,如何让Redis客户端实际执行命令?

这是我正在使用的代码片段。

var redis = require('redis');
var async = require('async');

var redisClient = redis.createClient(6379, '192.168.1.150');

var testData = generateTestData();

async.each(testData, function(item, callback) {

    var someData = JSON.stringify(item.data);

    redisClient.hset('item:'+item.key, 'hashKey', someData, function(err, reply) {
        console.log("Item was persisted.  Result: " +reply);
    });

    callback();
}, function(err) {
    if (err) {
        console.error(err);
    } else {
        console.log.info("Items have been persisted to Redis.");

    }
});

1 个答案:

答案 0 :(得分:0)

您可以调用eachLimit以确保您不会同时执行太多redisClient.hset调用。 为避免溢出调用堆栈,您可以执行setTimeout(callback, 0);而不是直接调用回调。

编辑:

忘掉我对setTimeout的看法。您需要做的就是在正确的位置调用回调。像这样:

redisClient.hset('item:'+item.key, 'hashKey', someData, function(err, reply) {
    console.log("Item was persisted.  Result: " +reply);
    callback();
});

您可能仍希望使用eachLimit并尝试哪种限制效果最佳。

顺便说一下 - async.each应该仅用于调度javascript事件队列中回调调用的代码(例如,计时器,网络等)。永远不要像在原始代码中那样立即在调用回调的代码上使用它。

编辑:

您可以实现自己的eachLimit函数,而不是数组将生成器作为第一个参数。然后编写生成器函数来创建测试数据。要使其工作,需要使用“node --harmony code.js”运行节点。

function eachLimit(generator, limit, iterator, callback) {
  var isError = false, j;

  function startNextSetOfActions() {
    var elems = [];
    for(var i = 0; i < limit; i++) {
      j = generator.next();
      if(j.done) break;
      elems.push(j.value);
    }
    var activeActions = elems.length;

    if(activeActions === 0) {
      callback(null);
    }

    elems.forEach(function(elem) {
      iterator(elem, function(err) {
        if(isError) return;
        else if(err) {
          callback(err);
          isError = true;
          return;
        }
        activeActions--;
        if(activeActions === 0) startNextSetOfActions();
      });
    });
  }

  startNextSetOfActions();
}

function* testData() {
  while(...) {
    yield new Data(...);
  }
}

eachLimit(testData(), 10, function(item, callback) {
    var someData = JSON.stringify(item.data);

    redisClient.hset('item:'+item.key, 'hashKey', someData, function(err, reply) {
        if(err) callback(err);
        else {
          console.log("Item was persisted.  Result: " +reply);
          callback();
        }
    });
}, function(err) {
    if (err) {
        console.error(err);
    } else {
        console.log.info("Items have been persisted to Redis.");

    }
});