복붙노트

[REDIS] 하나의 클라이언트에 의해 레디 스 시계 MULTI EXEC

REDIS

하나의 클라이언트에 의해 레디 스 시계 MULTI EXEC

RedisOnGo + 클라이언트로 node_redis에 나는 NodeJS + 익스프레스 + 레디 스를 사용하고 있습니다. 그래서 시험 보는 중이 동시성을 많이 기대하고 있습니다. 이 예는 익스프레스, 단지 필요한 물건을 포함하지 않습니다.

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

간부 콜백에서 점점 N 오류 및 마지막으로 "INC"변수 = 10-N을 받고 : 결과를 기대.

예기치 않은 결과 : 간부 콜백 0 오류를 받고 있지만, 결국 점점 "INC"변수 = 1.

시계는 내 코드가 작동하지 않습니다.

나는이 스레드 레디 스 및 멀티 시계 + 동시 사용자 수를 발견했다. 그들은 그것이 때문에 유일한 레디 스 클라이언트의 말한다.

그럼 난이 스레드해야 내가 각 연결에 대한 새로운 레디 스 클라이언트를 만들 발견? 그들은 각 거래를위한 새로운 클라이언트를 생성하는 것은 "확실히 권장되지 않는다"고 말한다. 나는 손실입니다.

또한 내가 레디 스 서버에 인증 할 필요가 있음을 유의하시기 바랍니다. 사전에 감사합니다!

EDITION 1 :

나는 각 WATCH-MULTI-EXEC 반복 전에 새로운 클라이언트 접속을 작성하여가 (내가 client.auth를 사용하지 않도록) 지역 레디 스 인스턴스를 사용하여 작업 할 수 있었다. 그것은 좋은 생각이지만, 결과는 지금 100 % 정확했는지 확실하지 않습니다.

EDITION 2 내가 client.on에 대한 client.auth 및 대기 할 각 WATCH-MULTI-EXEC 반복 전에 새로운 클라이언트 접속을 작성하고 있다면 그것은 작동했다.

문제는 아직도 내가 각 반복에 대한 새로운 클라이언트 연결을 만드는 것이 OK입니다 존재?

해결법

  1. ==============================

    1.귀하의 결과는 완전히 예측 가능하다. 그리고 바르게 그래서.

    귀하의 결과는 완전히 예측 가능하다. 그리고 바르게 그래서.

    명심하십시오 - Node.js를하는 것은 하나의 스레드 응용 프로그램입니다. Node.js를 비동기 입출력을 사용하지만, 명령이 엄격 순차 "요청 - 응답"레디 스 전송한다. 당신의 레디 스 서버에 하나의 연결을 사용하는 동안 엄격하게 실행 코드 및 사용자의 요청이 평행 그래서.

    당신의 코드를보고 :

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

    문제는이 단계를 수행 확인하려면 :

    출력은 것

        SET inc 0
        WATCH inc
    
        GET inc 
        .... get command more 9 times
    
        MULTI
        SET inc 1
        EXEC
        .... command block more 9 times
    

    그래서 당신은 정확하게 당신이 위의 쓴 결과를 얻을 : "간부 콜백 0 오류를 받고 있지만, 결국 점점"INC "변수 = 1.".

    그것은 당신이 각 반복에 대한 새로운 클라이언트 연결을 만드는 것이 OK인가?

    이 샘플 용 - 네, 그것을 해결해 문제. 일반적으로 - 그것은 많은 사람들이 "동시"쿼리가 실행하려는 방법에 따라 달라집니다. 레디 스 여전히 하나 레디 스 엔진에 동시 명령 배치로 단지 방법이 "동시"수단 있도록 스레드입니다.

    예를 들어, 사용이 연결되면 모니터는 다음과 같이 줄 수 :

     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
    

    그것의 매우 중요한 레디 스이 명령을 실행하는 방법을 이해합니다. 레디 스 단일 스레드이고, 모든 연결 모든 명령은 하나씩 연속하여 실행된다. 레디 스는 SHOULD MULTI 경우 희망 그래서 당신의 명령을 하나 개의 블록을 실행하는지 확인 (여기 경우 제품 존재하는 또 다른 연결은) 하나 개의 연결에서 해당 명령 행에서 실행됩니다 보장하지 않습니다 (만약 필요 그것을). 그런데 왜 시계 필요? 위의 내 레디 스 명령 봐. 당신은 서로 다른 연결에서 오는 그 명령을 혼합 볼 수 있습니다. 그리고 시계는 당신이 관리 할 수 ​​있습니다.

    이 아름답게 설명서에 설명했다. 읽어주세요!

  2. ==============================

    2.나는 마지막 질문을 얻었다.

    나는 마지막 질문을 얻었다.

    당신은 동시성을 위해 시계를 테스트 할 경우에, 나는 당신이 당신의 코드를 변경할 필요가 있다고 생각합니다. 우리는 알고있다. 시계는 값 연산을받지 못하고, 값의 변경을 모니터링 할 수 있습니다. 시계가 실패하지 않도록 그래서 현재 코드에서 모든 get 명령이 성공적으로 실행되고 0을 얻을, 그들은 1로 모든 설정 값을 INC 설정합니다 것은, (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)
            })
        })
    }
    

    내 PC에서 테스트.

  3. ==============================

    3.당신은 거래 / 원자 MULTI 작업을 사용하고 싶지만 당신은 내가 당신의 유일한 옵션은 LUA를 사용하고 알고 멀리, 공유 연결을 사용하여 그렇게합니다.

    당신은 거래 / 원자 MULTI 작업을 사용하고 싶지만 당신은 내가 당신의 유일한 옵션은 LUA를 사용하고 알고 멀리, 공유 연결을 사용하여 그렇게합니다.

    나는 여러 가지에 대한 레디 스에서 LUA 스크립트를 사용하고, LUA를 가진 것은 매우 편리하다, 전체 스크립트가 원자 적으로 실행하는 것이다. 이 수단은 당신이하고 있습니다 느린 LUA 스크립트가 서버를 사용하는 모든 느린 레디 스 경우 비록 알고 있어야합니다.

    서로 다른 키를 작동 할 경우에도 LUA를 사용할 때 당신은 당신이 출시되면 레디 스 클러스터를 사용할 수 없습니다 스크립트에 두 개 이상의 키를 사용하는 경우 또한,주의해야합니다. 당신의 LUA 스크립트는 단일 서버에서 모두에 액세스 할 수 없습니다 수 있도록이 클러스터를 사용하는 경우, 키가 다른 레디 스 프로세스에 배포 될 예정이다.

    MULTI가 클러스터에 다른 키를 설정 할 수 없습니다 때문에, 다중를 발행 할 때 어떤 경우에는, 레디 스 클러스터의 문제는 동일 할 것이다.

    건배,

    제이

  4. from https://stackoverflow.com/questions/15776955/redis-watch-multi-exec-by-one-client by cc-by-sa and MIT license