복붙노트

[REDIS] 원자 레디 스 데이터 구조에서 여러 값을 팝?

REDIS

원자 레디 스 데이터 구조에서 여러 값을 팝?

(+ 제거를 얻을) 여러 요소를 보여주고의 원자 작업을 수 있도록 해주는 레디 스 데이터 구조, 그것은이 포함되어 있습니까?

이 잘 알려진 SPOP 또는 RPOP가 있지만, 그들은 항상 하나의 값을 반환합니다. 세트 I /리스트로부터 제 N 값을 필요로 할 때 따라서, I 비싸다 명령 N 배를 호출 할 필요가있다. 하자가 설정 / 목록에있는 항목의 수백만을 포함 말한다. 목록에서 1000 오른쪽 대부분의 항목을 반환 돌아가 세트 또는 RPOPM "listName"1,000에서 1,000 임의 항목을 제거 할 SPOPM "에서는 setName"1000 같은이 무엇이든은 있습니까?

나는 SRANDMEMBER 및 LRANGE 같은 명령이 알고 있지만 데이터 구조에서 항목을 제거하지 마십시오. 그들은 개별적으로 삭제할 수 있습니다. 같은 데이터 구조에서 읽기 더 많은 고객이 존재하는 경우, 일부 항목은 읽지 않고 번 일부가 삭제 될 수있는 것보다 읽을 수 있습니다! 따라서, 자성 내 질문에 대해 무엇이다.

이러한 작업에 대한 시간 복잡도가 더 비싼 경우 또한, 나는 괜찮아요. 나는 (하자 1000 말, 앞의 예에서 N)는 N을 발행보다 더 비싼 것입니다 레디 스 서버에 별도의 요청을 의심한다.

또한 별도의 트랜잭션 지원에 대해 알고. 그러나 레디 스의 문서에서이 문장은 세트 (파괴적으로 그것을 읽는)를 수정 병렬 프로세스를 사용에서 저를 낙담 : 시계를 사용하는 경우, EXEC는 체크인 및 세트 메커니즘을 허용, 감시 키가 수정되지 않은 경우에만 명령을 실행합니다.

해결법

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

    1.레디 스 3.2부터 출발 명령 SPOP은 세트로부터 복수의 요소를 검색 [카운트] 인자를 갖는다.

    레디 스 3.2부터 출발 명령 SPOP은 세트로부터 복수의 요소를 검색 [카운트] 인자를 갖는다.

    http://redis.io/commands/spop#count-argument-extension 참조

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

    2.파이프 라인에 LTRIM과 LRANGE를 사용합니다. 파이프 라인은 하나의 원자 트랜잭션으로 실행됩니다. 당신이 그들 사이에 올 수있는 다른 클라이언트에서 다른 트랜잭션에 대한 기능없이 하나의 트랜잭션으로 LRANGE 및 LTRIM을 실행하고 있기 때문에 시계에 대한 위의 걱정, EXEC 여기에 적용되지 않습니다. 그것을 밖으로보십시오.

    파이프 라인에 LTRIM과 LRANGE를 사용합니다. 파이프 라인은 하나의 원자 트랜잭션으로 실행됩니다. 당신이 그들 사이에 올 수있는 다른 클라이언트에서 다른 트랜잭션에 대한 기능없이 하나의 트랜잭션으로 LRANGE 및 LTRIM을 실행하고 있기 때문에 시계에 대한 위의 걱정, EXEC 여기에 적용되지 않습니다. 그것을 밖으로보십시오.

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

    3.루아 대신 lrange 및 LTRIM 내장 명령을 사용하여 목록 모음에 대한 완벽한 예와 엘리의 응답에 확장하려면 :

    루아 대신 lrange 및 LTRIM 내장 명령을 사용하여 목록 모음에 대한 완벽한 예와 엘리의 응답에 확장하려면 :

    127.0.0.1:6379> lpush a 0 1 2 3 4 5 6 7 8 9
    (integer) 10
    127.0.0.1:6379> lrange a 0 3        # read 4 items off the top of the stack
    1) "9"
    2) "8"
    3) "7"
    4) "6"
    127.0.0.1:6379> ltrim a 4 -1        # remove those 4 items
    OK
    127.0.0.1:6379> lrange a 0 999      # remaining items
    1) "5"
    2) "4"
    3) "3"
    4) "2"
    5) "1"
    6) "0"
    

    당신이 작업이 원자 만들고 싶었다 경우 lrange 포장 및 멀티 및 간부 명령에 LTRIM 것이다.

    다른 곳에서 언급 한 바와 같이 또한, 당신은 아마 반환 항목 수없는 당신이 질문 항목의 수를 LTRIM한다. 예를 들면 당신이 그랬다면 lrange 0 99 그러나 당신이이 100 LTRIM (50) -1하지 LTRIM 것 50 개 항목을 가지고 -1.

    대신 스택의 큐의 의미를 구현 rpush와 lpush 교체하십시오.

  4. ==============================

    4.당신이 루아 스크립트를 원하는 경우,이 빠르고 쉽게해야합니다.

    당신이 루아 스크립트를 원하는 경우,이 빠르고 쉽게해야합니다.

    local result = redis.call('lrange',KEYS[1],0,ARGV[1]-1)
    redis.call('ltrim',KEYS[1],ARGV[1],-1)
    return result
    

    당신은 루프가 없습니다.

    최신 정보: 나는 다음과 같은 스크립트 (2.6) srandmember이 작업을 수행하려고 :

    local members = redis.call('srandmember', KEYS[1], ARGV[1])
    redis.call('srem', KEYS[1], table.concat(table, ' '))
    return members
    

    하지만 오류가 발생합니다 :

    error: -ERR Error running script (call to f_6188a714abd44c1c65513b9f7531e5312b72ec9b): 
    Write commands not allowed after non deterministic commands
    

    미래의 버전이 허용하지만하지 가정 나도 몰라. 나는 그것이 복제 문제가 될 것이라고 생각합니다.

  5. ==============================

    5.레디 스 4.0 이상을 훨씬 빠르고 루아 스크립트 또는 멀티 / 간부 인 파이프 라인보다 안전 처리와 함께 새로운 기능 및 데이터 유형의 모든 종류의 추가 모듈을 지원합니다.

    레디 스 4.0 이상을 훨씬 빠르고 루아 스크립트 또는 멀티 / 간부 인 파이프 라인보다 안전 처리와 함께 새로운 기능 및 데이터 유형의 모든 종류의 추가 모듈을 지원합니다.

    레디 스 연구소, 레디 스 뒤 현재 스폰서는 여기 REDEX라는 확장 모듈의 유용한 세트가 있습니다 : https://github.com/RedisLabsModules/redex

    rxlists 모듈은 원자 레디 스 목록에서 여러 값을 팝업 할 수 있도록 LMPOP 및 RMPOP 등 여러 목록 작업을 추가합니다. 논리는 여전히 (기본적으로 루프에서 하나의 팝업을하고) O (N)하지만, 당신이해야 할 일은 단지 그 사용자 지정 명령을 전송하면 모듈을 설치합니다. 내가 네트워크 트래픽 500MB의 +를 생성 한 번 문제없이에서 튀어 항목과 수천 수백만 목록에 그것을 사용할 수 있습니다.

  6. ==============================

    6.여기 레디 스-PY와 파이프 라인을 사용하여이를 달성 할 수있는 파이썬 코드 조각입니다 :

    여기 레디 스-PY와 파이프 라인을 사용하여이를 달성 할 수있는 파이썬 코드 조각입니다 :

    from redis import StrictRedis
    
    client = StrictRedis()
    
    def get_messages(q_name, prefetch_count=100):
        pipe = client.pipeline()
        pipe.lrange(q_name, 0, prefetch_count - 1)  # Get msgs (w/o pop)
        pipe.ltrim(q_name, prefetch_count, -1)  # Trim (pop) list to new value
        messages, trim_success = pipe.execute()
        return messages
    

    나는 목록 큐가 prefetch_count보다 작은 특히, 심지어 파이프 라인, 난 그냥 팝의 루프 A A를 할 수 있지만, 그 효율적하지 않을 것이라고 생각했다. 난 당신이보고 싶을 경우 전체 RedisQueue 클래스는 여기에 구현했습니다. 희망이 도움이!

  7. ==============================

    7.난 당신이 레디 스에서 LUA 지원 봐야한다 생각합니다. 당신은 레디 스에 LUA 스크립트 및 실행하는 그것을 작성하는 경우, 그것 (레디 스이기 때문에 단일 스레드) 원자 것을 보장한다. 어떤 쿼리가 당신의 LUA 스크립트의 종료 전에 수행되지 않습니다 (예 : 당신은 LUA에 큰 작업을 구현할 수 없습니다 또는 레디 스 느린 얻을 것이다).

    난 당신이 레디 스에서 LUA 지원 봐야한다 생각합니다. 당신은 레디 스에 LUA 스크립트 및 실행하는 그것을 작성하는 경우, 그것 (레디 스이기 때문에 단일 스레드) 원자 것을 보장한다. 어떤 쿼리가 당신의 LUA 스크립트의 종료 전에 수행되지 않습니다 (예 : 당신은 LUA에 큰 작업을 구현할 수 없습니다 또는 레디 스 느린 얻을 것이다).

    그래서, 당신은 당신의 SPOP 및 RPOP를 추가하려면이 스크립트에서, 당신은 예에 대한 LUA 배열의 각 레디 스 명령의 결과를 추가 할 수 있습니다 다음 레디 스 클라이언트로 배열을 돌려줍니다.

    무엇 문서 MULTI에 대해 말하는 것은 낙관적 잠금 감시 값이 수정되지 않습니다 때까지 시계 다 일을하고 수단 다시 시도합니다 있다는 것입니다. 어떤 방식으로 쿼리의 순서를 '세계를 정지'고 먼저 실행 될 : 당신이 지켜 값에 많은 쓰기가 있다면, 그것은 '비관적'이 (POSTGRESQL, MYSQL ... 많은 SQL 데이터베이스 같은) 락킹보다 느린 것 . 비관적 잠금 레디 스에서 구현되지 않습니다,하지만 당신이 원하는 경우에 당신은 그것을 구현할 수 있지만, 복잡하고 어쩌면 당신은 필요하지 않습니다 (: 낙관적 꽤 충분해야이 값에없는 많은 쓰기를).

  8. ==============================

    8.당신은 아마 이런 루아 스크립트 (script.lua)을 시도 할 수 있습니다 :

    당신은 아마 이런 루아 스크립트 (script.lua)을 시도 할 수 있습니다 :

    local result = {}
    for i = 0 , ARGV[1] do
        local val = redis.call('RPOP',KEYS[1])
        if val then
            table.insert(result,val)
        end
    end
    return result
    

    당신은 이런 식으로 호출 할 수 있습니다 :

    redis-cli  eval "$(cat script.lua)" 1 "listName" 1000
    
  9. from https://stackoverflow.com/questions/20621775/pop-multiple-values-from-redis-data-structure-atomically by cc-by-sa and MIT license