首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Redis由一个客户监视多个主管

Redis由一个客户监视多个主管
EN

Stack Overflow用户
提问于 2013-04-03 00:38:38
回答 4查看 12.6K关注 0票数 10

我使用NodeJS + Express + Redis在RedisOnGo + node_redis上作为客户端。我希望有很多并发性,所以试着测试一下手表。这个例子不包含Express,只是必要的东西。

代码语言:javascript
复制
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)
            })
        })
    }
})

预期结果:在exec回调中获取N个错误,最后得到"inc“变量= 10-N。

意外结果:在exec回调中获得0错误,但最终得到"inc“变量= 1。

手表不适用于我的密码。

我找到了这个线程redis and watch + multi allows concurrent users。他们说这是因为唯一的红人客户。

然后我找到了这个线程Should I create a new Redis client for each connection?。他们说,为每个事务生成一个新的客户端“绝对不建议”。我迷路了。

请注意,我必须对Redis服务器进行身份验证。提前感谢!

第1版:

我能够通过在每次多个EXEC迭代之前创建一个新的客户端连接来使用本地Redis实例(所以我不使用client.auth)来使其工作。不确定它是否是好的,但现在的结果是100%的准确性。

第2版使它工作,如果我创建一个新的客户端连接之前,每次手表-多EXEC迭代,然后做client.auth和等待client.on。

问题仍然存在,我是否可以为每次迭代创建新的客户端连接?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-11-25 06:51:39

你的结果是完全可以预测的。这是正确的。

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

看看你的代码:

代码语言:javascript
复制
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) 
            })
        })
    }
})

要确认这一点,请执行以下步骤:

  1. 连接到redis并执行monitor命令。
  2. 运行node.js应用程序

输出将是

代码语言:javascript
复制
    SET inc 0
    WATCH inc

    GET inc 
    .... get command more 9 times

    MULTI
    SET inc 1
    EXEC
    .... command block more 9 times

这样您就可以得到上面所写的结果:“在exec回调中获得0错误,但最终得到"inc”变量=1。

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

对于这个样本-是的,它解决了你的问题。一般情况下,这取决于要运行多少“并发”查询。Redis仍然是一个线程,所以这个“并发”意味着将并发命令批处理到redis引擎。

例如,如果使用2个连接,monitor可以提供如下内容:

代码语言:javascript
复制
 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命令。您可以看到来自不同连接的命令是混合的。看着让你处理这件事。

documentation中给出了很好的解释。请看!

票数 22
EN

Stack Overflow用户

发布于 2013-11-26 08:46:13

我终于得到你的问题了。

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

在这种情况下,我们需要确保不仅write操作受到保护,而且read也受到保护。在设置inc之前,您需要watch并修改另一个键,即悲观锁,然后我们可以获取和更改inc。通过这种方式,它将确保您的期望。

代码语言:javascript
复制
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)
        })
    })
}

我在我的电脑上测试过。

2013年-11-26 18:51:09.389控制台- 1,'OK‘信息控制台- 2,'OK’信息控制台- 3,'OK‘信息控制台- 4,'OK’信息控制台- 5,'OK‘信息控制台- 6,'OK’信息控制台- 7,'OK‘信息控制台- 8,'OK’信息控制台- 9,'OK‘信息控制台- 10,'OK’信息控制台‘OK’

票数 2
EN

Stack Overflow用户

发布于 2013-11-26 12:19:51

如果您想使用事务/原子多操作,但希望使用共享连接,据我所知,您唯一的选择是使用LUA。

我在redis中使用LUA脚本来处理许多事情,而LUA的问题是整个脚本将原子地执行,这是相当方便的。但是,您必须意识到,这意味着如果您有一个缓慢的LUA脚本,您正在使每个使用您的服务器的人的红包变慢。

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

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

干杯,

J

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15776955

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档