javascript Redis WATCH MULTI EXEC 由一个客户端执行

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15776955/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 02:04:34  来源:igfitidea点击:

Redis WATCH MULTI EXEC by one client

javascriptnode.jsredisnode-redis

提问by igorpavlov

I am using NodeJS + Express + Redis on RedisOnGo + node_redis as a client. I expect a lot of concurrency, so trying to test WATCH. This example won't contain Express, just necessary stuff.

我在 RedisOnGo + node_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)
            })
        })
    }
})

Expecting result: getting N errors in exec callbacks and finally getting "inc" variable = 10-N.

预期结果:在 exec 回调中出现 N 个错误,最终得到“inc”变量 = 10-N。

Unexpected result: getting 0 errors in exec callbacks but finally getting "inc" variable = 1.

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

Watch doesn't work with my code.

Watch 不适用于我的代码。

I have found this thread redis and watch + multi allows concurrent users. They say it is because of the only redis client.

我发现这个线程redis 和 watch + multi 允许并发用户。他们说这是因为唯一的redis客户端。

Then I found this thread Should I create a new Redis client for each connection?. They say that generating a new client for each transaction "is definitely not recommended". I am lost.

然后我找到了这个线程我应该为每个连接创建一个新的Redis客户端吗?. 他们说“绝对不推荐”为每笔交易生成一个新客户端。我搞不清楚了。

Please also note, that I have to authenticate to Redis server. Thanks in advance!

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

EDITION 1:

第 1 版:

I was able to make it work using local Redis instance (so I do not use client.auth) by creating a new client connection before each WATCH-MULTI-EXEC iteration. Not sure if it is good though, but results now are 100% accurate.

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

EDITION 2Made it work if I create a new client connection before each WATCH-MULTI-EXEC iteration and then do client.auth and wait for client.on.

版本 2如果我在每次 WATCH-MULTI-EXEC 迭代之前创建一个新的客户端连接,然后执行 client.auth 并等待 client.on,它就可以工作。

The question still exists, is it OK that I create new client connections for each iteration?

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

回答by misterion

Your result is entirely predictable. And rightly so.

你的结果是完全可以预测的。理所当然。

Keep in mind - node.js is one thread application. Node.js use asynchronous input-output, but the commands should be sent in redis strictly sequential "request-response". So your code and your requests executed strictly parallel while your are using just one connection to redis server.

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

Look at your code:

看看你的代码:

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) 
            })
        })
    }
})

To confirm this do this steps:

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

  1. Connect to redis and execute monitorcommand.
  2. Run your node.js application
  1. 连接到redis并执行monitor命令。
  2. 运行您的 node.js 应用程序

The output would be

输出将是

    SET inc 0
    WATCH inc

    GET inc 
    .... get command more 9 times

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

So that you get exactly the results that you wrote above: "getting 0 errors in exec callbacks but finally getting "inc" variable = 1.".

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

Is it OK that you create new client connections for each iteration?

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

For this sample - yes, its solves your problem. In general - it depends on how many "concurrent" query you want to run. Redis is still one threaded so this "concurrent" means just way to concurrent command batch to redis engine.

对于此示例 - 是的,它解决了您的问题。一般来说 - 这取决于您要运行多少个“并发”查询。Redis 仍然是一个线程,因此这种“并发”意味着将命令批处理并发到 Redis 引擎的唯一方式。

For example, if use 2 connections the monitorcould give something like this:

例如,如果使用 2 个连接,则monitor可能会给出如下内容:

 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

Its very important to understand how redis execute your commands. Redis is single threaded, all command from all connection executed one-by-one in a row. Redis does not guarantee that command from one connection would be executed in a row (if here is another connections present) so your should MULTI if want be sure that your commands executed one block (if need it). But why WATCH needed? Look at my redis commands above. You can see that command coming from different connections are mixed. And watch allow you to manage this.

了解 redis 如何执行您的命令非常重要。Redis 是单线程的,所有连接中的所有命令都一一执行。Redis 不保证来自一个连接的命令会连续执行(如果这里存在另一个连接),因此您应该 MULTI 如果要确保您的命令执行一个块(如果需要)。但是为什么需要WA​​TCH?看看我上面的 redis 命令。您可以看到来自不同连接的命令混合在一起。并且手表允许您管理此。

This beautifully explained in the documentation. Please read it!

这在文档中得到了很好的解释。请阅读!

回答by Henry Leu

I got your question finally.

我终于知道你的问题了。

If you want to test WATCHfor concurrency, I think you need to change your code. as we know. WATCHonly monitor changing of value, not getting value operation. so in your current code, all your getcommand will be executed successfully and get 0, then they will set incto 1. all the set value are the same (1), so watch won't fail.

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

In the case, we need to ensure not only writeoperation is protected, but also read. before you set inc, you need to watchand modify another key which is as a pessimistic lock, and then we can get and change inc. in this way, it will make sure your expectation.

在这种情况下,我们需要确保不仅write操作受到保护,而且read. 在设置之前inc,需要watch修改另一个作为悲观锁的key ,然后我们就可以获取和修改了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)
        })
    })
}

I tested it in my PC.

我在我的电脑上测试过。

[2013-11-26 18:51:09.389] [INFO] console - [ 1, 'OK' ]

[2013-11-26 18:51:09.390] [INFO] console - [ 2, 'OK' ]

[2013-11-26 18:51:09.390] [INFO] console - [ 3, 'OK' ]

[2013-11-26 18:51:09.390] [INFO] console - [ 4, 'OK' ]

[2013-11-26 18:51:09.391] [INFO] console - [ 5, 'OK' ]

[2013-11-26 18:51:09.391] [INFO] console - [ 6, 'OK' ]

[2013-11-26 18:51:09.392] [INFO] console - [ 7, 'OK' ]

[2013-11-26 18:51:09.392] [INFO] console - [ 8, 'OK' ]

[2013-11-26 18:51:09.393] [INFO] console - [ 9, 'OK' ]

[2013-11-26 18:51:09.393] [INFO] console - [ 10, 'OK' ]

[2013-11-26 18:51:09.389] [INFO] 控制台 - [ 1, 'OK' ]

[2013-11-26 18:51:09.390] [INFO] 控制台 - [ 2, 'OK' ]

[2013-11-26 18:51:09.390] [INFO] 控制台 - [ 3, 'OK' ]

[2013-11-26 18:51:09.390] [INFO] 控制台 - [ 4, 'OK' ]

[2013-11-26 18:51:09.391] [INFO] 控制台 - [ 5, 'OK' ]

[2013-11-26 18:51:09.391] [INFO] 控制台 - [ 6, 'OK' ]

[2013-11-26 18:51:09.392] [INFO] 控制台 - [ 7, 'OK' ]

[2013-11-26 18:51:09.392] [INFO] 控制台 - [ 8, 'OK' ]

[2013-11-26 18:51:09.393] [INFO] 控制台 - [ 9, 'OK' ]

[2013-11-26 18:51:09.393] [INFO] 控制台 - [ 10, 'OK' ]

回答by Javier Ramirez

If you want to use transaction/atomic MULTI operations but you want to do so using a shared connection, as far as I know your only option is using LUA.

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

I use LUA scripting within redis for a number of things, and the thing with LUA is the whole script will execute atomically, which is quite convenient. You have to be aware though that this means if you have a slow LUA script you are making redis slow for everyone using your server.

我在 redis 中使用 LUA 脚本做很多事情,而 LUA 的事情是整个脚本将原子地执行,这非常方便。您必须注意,这意味着如果您有一个缓慢的 LUA 脚本,那么对于使用您的服务器的每个人来说,redis 都会变慢。

Also, when using LUA even if you can operate on different keys, be aware if you use more than one key in your script you won't be able to use Redis cluster once it is released. This is due to, when using a cluster, keys will be distributed to different Redis processes so your LUA script might not have access to all of them on a single server.

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

In any case, the problem with redis cluster would be the same when issuing a MULTI, since MULTI won't be allowed to set different keys on a cluster.

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

Cheers,

干杯,

j

j