Redis - 在带有承诺的事务中插入数组中的值(蓝鸟)

Posted

技术标签:

【中文标题】Redis - 在带有承诺的事务中插入数组中的值(蓝鸟)【英文标题】:Redis - Insert values from array in a transaction with promises (bluebird) 【发布时间】:2015-07-19 23:38:08 【问题描述】:

我有一个这样的数组:

var names = ['Irina', 'Michael', 'Carl'];

我想使用带有承诺的事务将它们插入到 redis 中(我不知道其他方式)。但我对如何做到这一点感到困惑;这是我的代码:

var Promise = require("bluebird");
var redis = require("redis");

Promise.promisifyAll(redis.RedisClient.prototype);
Promise.promisifyAll(redis.Multi.prototype);

var client = redis.createClient(), multi;

var names = ['Irina', 'Michael', 'Carl'];

var result = names.map(function(item) 
    client.watch('user:id');
    client.getAsync('user:id', function(err, data) 
      var multi = client.multi();
      var user_id = parseInt(data) + 1;
      multi.hmsetAsync('user:' + user_id, 'username', item, 'about', 'love to coding');
      multi.incrAsync('user:id');
      multi.execAsync(function(err,data)
        console.log(data);
      );
    );
);

Promise.all(result).then(function(response) 
    console.log(response);
);

但这不起作用(:

编辑:这是应用程序抛出的错误:

未处理的拒绝错误:ERR 'get' 命令的参数数量错误

有人可以帮帮我吗!


编辑 2: 我更改了代码,但现在只保存数组的最后一个值:

client.watch('user:id');
var result = names.map(function(item)         
    var multi = client.multi();
    client.getAsync('user:id').then(function(value) 
        var user_id = parseInt(value) + 1;
        return user_id;
    ).then(function(user_id) 
        multi.hmsetAsync('user:' + user_id, 'username', item, 'about', 'love to coding');
        multi.incrAsync('user:id');
    ).then(function() 
        multi.execAsync().spread(function(err,data)
            console.log(data);
        );
    );
);

编辑 3:

使用redis' MONITOR时,输出如下:

[0 127.0.0.1:54439] "info"
[0 127.0.0.1:54439] "watch" "user:id"
[0 127.0.0.1:54439] "get" "user:id"
[0 127.0.0.1:54439] "get" "user:id"
[0 127.0.0.1:54439] "get" "user:id"
[0 127.0.0.1:54439] "MULTI"
[0 127.0.0.1:54439] "hmset" "user:88" "username" "Irina" "about" "love to coding"
[0 127.0.0.1:54439] "incr" "user:id"
[0 127.0.0.1:54439] "EXEC"
[0 127.0.0.1:54439] "MULTI"
[0 127.0.0.1:54439] "hmset" "user:88" "username" "Michael" "about" "love to coding"
[0 127.0.0.1:54439] "incr" "user:id"
[0 127.0.0.1:54439] "EXEC"
[0 127.0.0.1:54439] "MULTI"
[0 127.0.0.1:54439] "hmset" "user:88" "username" "Carl" "about" "love to coding"
[0 127.0.0.1:54439] "incr" "user:id"
[0 127.0.0.1:54439] "EXEC"

执行get user:id 3 次,然后执行其他方法。为什么?

【问题讨论】:

你为什么要通过回调调用getAsync 我已经用我所做的更改编辑了我的问题,但仍然遇到一些问题。 对于这个用例来说,最好的事情是利用 Lua 并在服务器本身内执行 【参考方案1】:

我以这种方式解决了我的问题:

client.watch('user:id');
Promise.each(names, function(item) 
    var multi = client.multi();
    return client.getAsync('user:id').then(function(value) 
        var user_id = parseInt(value) + 1;
        return user_id;    
    ).then(function(user_id) 
        multi.hmsetAsync('user:' + user_id, 'username', item, 'about', 'love to coding');
        multi.incrAsync('user:id');
    ).then(function() 
        return multi.execAsync().spread(function(err,data)
            console.log(data);
        );
    );
)
.then(function() 
    console.log("Ended process ...");
);

诀窍是改变:

var result = names.map(function(item) ...);

承诺:

Promise.each(names, function(item) ...);

然后将client.getAsync(user:id)返回给它。

这是redis的MONITOR的输出:

[0 127.0.0.1:53290] "info"
[0 127.0.0.1:53290] "watch" "user:id"
[0 127.0.0.1:53290] "get" "user:id"
[0 127.0.0.1:53290] "MULTI"
[0 127.0.0.1:53290] "hmset" "user:88" "username" "Irina" "about" "love to coding"
[0 127.0.0.1:53290] "incr" "user:id"
[0 127.0.0.1:53290] "EXEC"
[0 127.0.0.1:53290] "get" "user:id"
[0 127.0.0.1:53290] "MULTI"
[0 127.0.0.1:53290] "hmset" "user:89" "username" "Michael" "about" "love to coding"
[0 127.0.0.1:53290] "incr" "user:id"
[0 127.0.0.1:53290] "EXEC"
[0 127.0.0.1:53290] "get" "user:id"
[0 127.0.0.1:53290] "MULTI"
[0 127.0.0.1:53290] "hmset" "user:90" "username" "Carl" "about" "love to coding"
[0 127.0.0.1:53290] "incr" "user:id"
[0 127.0.0.1:53290] "EXEC"

惊人,但真实! (线索:https://***.com/a/25129878/2954267)

【讨论】:

【参考方案2】:

免责声明:不是 JS 专家,但我会尽力帮助 :)

您似乎希望每个名称都有一个唯一的 ID。本着KISS 的精神,我会尝试执行以下不需要任何事务行为的流程:

    id = INCR 用户:id HMSET 用户:...

这里的“风险”是,如果您的工人在 1 和 2 之间死亡,您会“花费”一个 ID。如果你能忍受,你的可能应该看起来像这样(抱歉,未经测试 - 请参阅免责声明;)):

var result = names.map(function(item) 
    client.incrAsync('user:id').then(function(user_id) 
        client.hmsetAsync('user:' + parseInt(user_id), 'username', item, 'about', 'love to coding');
    ).then(function(err) 
        console.log(err);
    );
);

更新 1:但是由于您正在修改 for 事务,我猜想姓氏的更新,特别是对 INCR 的调用,会破坏以前的 WATCH。由于您没有导致错误,因此您看不到这一点。您还可以在执行代码时使用 Redis 的 MONITOR 来调试行为。

更新 2:我猜这就是 Node 异步处理的方式 - 首先它会触发 GET 并在需要等待响应时停止。然后第一个响应(对第一个 GET)得到处理,然后是第二个......除了弄乱你的预期逻辑之外,这也导致只有第一批享受 MULTI - 另外两个在他们之前没有 MULTI 语句相对 EXEC。

【讨论】:

是的,但我想用交易来做! 为什么交易有问题?为什么multi.execAsync() 不起作用? 凭直觉更新了答案 添加更多想法 - 您需要事务将所有名称包含在一起还是仅包含名称和计数器增量? 另外,没有什么比对我自己的回答投反对票更重要的了

以上是关于Redis - 在带有承诺的事务中插入数组中的值(蓝鸟)的主要内容,如果未能解决你的问题,请参考以下文章

Redis事务

Redis 事务

Redis 事务

Redis 事务

Redis 事务

Redis 事务