Node.js、Redis——检查键是否存在,如果不存在则创建新键

5

我是新手,对于nodejs的事件系统不太熟悉。无法找到错误,请给予建议。 我需要完成一个简单的任务 - 检查标签是否存在,如果不存在,则设置新的键和标签信息。 问题是-当我第一次运行脚本时,它总是返回“键不存在”。检查redisdb键 - 它创建了许多标签。 以下是我的代码:

for (x = 0; x < rows.length; x++) {
    if (rows[x].term_taxonomy_id != 1) {
        var taxonomy = findOne(rterms, rows[x].term_taxonomy_id);
        rc.exists('tag:' + taxonomy.name, function (err, rexists) {
            if (rexists == false) {
                rc.incr('tags:count', function (err, id) {
                    console.log(taxonomy.name+' not exists. result ' + rexists);
                    rc.set('tag:' + taxonomy.name,id);
                    rc.hmset('tag:' + id,
                        'id', id,
                        'title',taxonomy.name,
                        'url', taxonomy.slug
                    );
                });//incr
            }else{
                console.log(taxonomy.name+' exists!'+rexists);
            };
        });//exists
    };//ifrows
});

以下是另一个示例

var tags = [
  "apple",
  "tiger",
  "mouse",
  "apple",
  "apple",
  "apple",
  "tiger",
  "mouse",
  "mouse",
];
var count =0;
Object.keys(tags).forEach (function (tag) {
  rc.get("tag:"+tags[tag],function(err,rr){
    console.log("get tag "+tags[tag]+" result code "+rr);
    if (rr == null) {
      rc.set("tag:"+tags[tag],"info",function(err,rr){
        count++;
        console.log('set tag '+tags[tag]+' '+rr+' objects count '+count);
      });
    };
  });
})

输出结果:
get tag apple result code null
get tag tiger result code null
get tag mouse result code null
get tag apple result code null
get tag apple result code null
get tag apple result code null
get tag tiger result code null
get tag mouse result code null
get tag mouse result code null
set tag apple OK objects count 1
set tag tiger OK objects count 2
set tag mouse OK objects count 3
set tag apple OK objects count 4
set tag apple OK objects count 5
set tag apple OK objects count 6
set tag tiger OK objects count 7
set tag mouse OK objects count 8
set tag mouse OK objects count 9

看起来nodejs执行所有'get'命令,然后才执行'set'命令。所以……我理解,这都是因为异步操作的缘故。但是如何让它正常工作呢?

1个回答

5

这段代码中至少存在两个问题:

  • 第一个问题与JavaScript闭包管理相关。循环体不会创建作用域。在JavaScript中,变量的作用域是函数级别,而不是块级别。您需要在循环内部引入一些函数来强制创建正确的闭包。更多信息请参见此处

  • 第二个问题是exists和set命令之间的竞争条件。如果您有多个Redis连接在同一键上运行exists和set命令,则可能会发生某种冲突。您应该使用setnx来执行检查和设置的原子操作,而不是使用exists和set。

考虑到您的第二个示例,通过使用forEach解决了闭包问题,但由于语言的异步性质,仍然会生成所有get操作再进行set操作。

如果您真的想要按顺序执行所有的get和set操作(这将慢得多),那么可以使用一些函数式编程来使用递归实现循环。

示例

此程序:

var redis = require('redis')
var rc = redis.createClient(6379, 'localhost');

var tags = [
  "apple",
  "tiger",
  "mouse",
  "apple",
  "apple",
  "apple",
  "tiger",
  "mouse",
  "mouse",
];

var count = 0;

function loop(tags) {
  function rec_loop(tags,i) {
     if ( i >= tags.length )
        return
     rc.get("tag:"+tags[i],function(err,rr) {
        console.log("get tag "+tags[i]+" result code "+rr);
        if ( rr == null ) {
           rc.set("tag:"+tags[i],"info",function(err,rr) {
              count++;
              console.log('set tag '+tags[i]+' '+rr+' objects count '+count);
              rec_loop(tags,++i)
           })
        } else
          rec_loop(tags,++i)
     })
  }
  rec_loop(tags,0)
}

loop(tags)

显示:

get tag apple result code null
set tag apple OK objects count 1
get tag tiger result code null
set tag tiger OK objects count 2
get tag mouse result code null
set tag mouse OK objects count 3
get tag apple result code info
get tag apple result code info
get tag apple result code info
get tag tiger result code info
get tag mouse result code info
get tag mouse result code info

请注意,这个例子中仍然存在竞态条件。您应该使用setnx来实现这种检查和设置操作。


谢谢。我确定问题在程序执行流程中。我进行了另一个测试,得到了相同的问题。 - Mike Sp
在我之前的回复中添加了一个示例。 - Didier Spezia
谢谢您提供这个很好的例子。在经过多年的线性编程后,异步编程有点难以理解。 - Mike Sp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接