Javascript Redis通过一个客户端监视多执行

Javascript Redis通过一个客户端监视多执行,javascript,node.js,redis,node-redis,Javascript,Node.js,Redis,Node Redis,我正在RedisOnGo+node\u Redis上使用NodeJS+Express+Redis作为客户端。我希望有很多并发性,所以尝试测试WATCH。本例不包含Express,只包含必要的内容 var redis = require("redis") var rc = redis.createClient(config.redis.port, config.redis.host) rc.auth(config.redis.hash, function(err) { if (err)

我正在RedisOnGo+node\u Redis上使用NodeJS+Express+Redis作为客户端。我希望有很多并发性,所以尝试测试WATCH。本例不包含Express,只包含必要的内容

var redis = require("redis")
var rc = redis.createClient(config.redis.port, config.redis.host)

rc.auth(config.redis.hash, function(err) {
    if (err) {
        throw err
    }
})

rc.on('ready', function () {
    rc.set("inc",0)
    for(var i=1;i<=10;i++){
        rc.watch("inc")
        rc.get("inc",function(err,data){
            var multi = rc.multi()
            data++ // I do know I can use rc.incr(), this is just for example
            multi.set("inc",data)
            multi.exec(function(err,replies){
                console.log(replies)
            })
        })
    }
})
var redis=require(“redis”)
var rc=redis.createClient(config.redis.port,config.redis.host)
rc.auth(config.redis.hash,函数(err){
如果(错误){
失误
}
})
rc.on('ready',function(){
rc.set(“inc”,0)

对于(var i=1;i来说,你的结果是完全可以预测的,这是正确的

请记住-node.js是一个线程应用程序。node.js使用异步输入输出,但命令应以redis严格顺序的“请求-响应”发送。因此,当您只使用一个到redis服务器的连接时,代码和请求严格并行执行

看看你的代码:

rc.on('ready', function () {
    rc.set("inc",0)
    for(var i = 1; i <= 10; i++){
        rc.watch("inc")
        //10 times row by row call get function. It`s realy means that your written
        //in an asynchronous style code executed strict in series. You are using just
        //one connection - so all command would be executed one by one.
        rc.get("inc",function(err,data){
            //Your data variable data = 0 for each if request.
            var multi = rc.multi()
            data++ //This operation is not atomic for redis so your always has data = 1
            multi.set("inc",data) //and set it
            multi.exec(function(err,replies){
                console.log(replies) 
            })
        })
    }
})
这样您就可以得到上面所写的结果:“在exec回调中得到0个错误,但最终得到“inc”variable=1。”

您可以为每个迭代创建新的客户端连接吗?

对于这个示例,是的,它解决了您的问题。一般来说,它取决于您想要运行多少个“并发”查询。Redis仍然是一个线程,所以这个“并发”意味着向Redis引擎执行并发命令批处理

例如,如果使用2个连接,
监视器
可以给出如下结果:

 1 SET inc 0 //from 1st connection
 2 WATCH inc //from 1st connection
 3 SET inc 0 //from 2nd connection            
 4 GET inc //from 1nd connection            
 5 WATCH int //from 2nd connection       
 6 GET inc //from 2nd connection                 
 7 MULTI //from 1st connection           
 8 SET inc 1 //from 1st connection    
 9 MULTI //from 2nd connection           
10 SET inc 1 //from 2nd connection           
11 EXEC //from 1st failed becouse of 2nd connection SET inc 0 (line 3) 
        //was executed after WATCH (line 2) 
12 EXEC //success becouse of MULTI from 1st connection was failed and SET inc 1 from first 
        //connection was not executed

-------------------------------------------------------------------------------> time 
               |   |    |  |   |     |     |    |   |     |    |         |
connection 1  set watch | get  |     |   multi set  |     |   exec(fail) |
connection 2          set    watch  get            multi set            exec
理解redis如何执行您的命令非常重要。redis是单线程的,所有连接中的所有命令在一行中逐个执行。redis不保证一个连接中的命令在一行中执行(如果存在其他连接)因此,如果需要,您应该确保您的命令执行一个块(如果需要)。但是为什么需要监视?请查看上面的我的redis命令。您可以看到来自不同连接的命令是混合的。监视允许您管理这一点


这篇文章在文章中解释得很好。请阅读!我终于收到了你的问题

如果您想测试WATCH的并发性,我认为您需要更改代码。正如我们所知,WATCH只监视值的更改,而不是获取值操作。因此,在您当前的代码中,所有
get
命令都将成功执行并获取
0
,然后它们将
inc
设置为
1
。所有设置值都相同(
1
),因此手表不会出现故障

在这种情况下,我们不仅需要确保
写入
操作受到保护,还需要确保
读取
。在设置
inc
之前,您需要
观察
,并修改另一个作为悲观锁的键,然后我们可以获取并更改
inc
。这样,它将确保您的期望

rc.set("inc",0)
for(var i=1;i<=10;i++){
    rc.watch("inc-lock")
    rc.get("inc",function(err,data){
        var multi = rc.multi()
        data++
        multi.incr("inc-lock")
        multi.set("inc",data)
        multi.exec(function(err,replies){
            console.log(replies)
        })
    })
}
rc.set(“inc”,0)

对于(var i=1;i,如果您想使用事务/原子多操作,但希望使用共享连接,就我所知,您唯一的选择是使用LUA

我在redis中使用LUA脚本做了很多事情,LUA的事情是整个脚本将以原子方式执行,这是非常方便的。但你必须意识到,这意味着如果你有一个缓慢的LUA脚本,你会使每个使用服务器的人的redis变慢

此外,在使用LUA时,即使您可以操作不同的密钥,也要注意,如果您在脚本中使用多个密钥,那么一旦Redis cluster发布,您将无法使用它。这是因为,在使用集群时,密钥将分发到不同的Redis进程,因此您的LUA脚本可能无法在单个服务器上访问所有这些密钥。

在任何情况下,当发出MULTI时,redis集群的问题都是一样的,因为MULTI不允许在集群上设置不同的键

干杯


j

谢谢你的回答。但是,这并没有解决我的问题。我了解Redis的排队过程,但这里的问题很简单:我有一个单节点过程,如果我在应用程序开始时连接到Redis一次,并尝试将此连接用于所有查询-WATCH似乎不起作用。如果我创建了一个w连接每次我执行“事务”时,WATCH都会工作,并且实际上会查看密钥。但我不想每次需要向Redis发出请求时都连接到Redis。这就是问题所在。再次感谢您的时间,但仍然不正确。在代码中,我写了一条注释“//我知道我可以使用rc.incr(),这只是一个示例”。我知道incr()是一个原子操作。但是,在这段代码中可以有很多不同的非原子操作,比如GET->SET。另外,您不需要将WATCH与incr()一起使用。我猜你不明白我的代码是什么意思。我增加了inc lock键来添加一个手表锁。这与我们真正的inc逻辑无关。两件事。顺便说一句,我的代码在集群环境中会发出错误,这会造成真正的竞争条件。这将证明“watch”是安全的。我用这个额外的命令执行了你的代码:
setTimeout(function(){rc.get(“inc”,函数(err,data){console.log('inc value:'+data);});},3000);
结果是[1',OK'][2',OK'][3',OK'][4',OK'][5',OK'][6',OK'][7',OK'][8',OK'][9',OK'][10',OK']inc value:1我期待inc value:10谢谢,你的回答似乎很有解释性,你已经正确理解了我的问题。如果两天内没有更多答案,赏金就是你的了。LUA在这里绝对是一个解决方案,谢谢。不确定现在谁会收到赏金=)
rc.set("inc",0)
for(var i=1;i<=10;i++){
    rc.watch("inc-lock")
    rc.get("inc",function(err,data){
        var multi = rc.multi()
        data++
        multi.incr("inc-lock")
        multi.set("inc",data)
        multi.exec(function(err,replies){
            console.log(replies)
        })
    })
}