Python의 Redis 목록에서 여러 항목을 원자적으로 팝핑

2942 단어 pythonredisatomicity

뭐하는거야?



Redis의 목록에서 여러 항목을 원자적으로 팝하고 제거하려고 합니다.

뭐?



목록을 고려하십시오: 0 1 2 3 4 5처음 3개 항목의 결과를 반환한 다음 해당 항목을 제거하고 싶습니다. 또한 결과를 반환하고 목록에서 결과를 제거하는 사이에 목록이 변경되지 않도록 해야 합니다.



다음 예를 고려하십시오.

> RPUSH mykey 0 1 2 3 4 5
(integer) 6
> LRANGE mykey 0 2
1) "0"
2) "1"
3) "2"
> LTRIM mykey 3 -1
OK
> LRANGE mykey 0 99
1) "3"
2) "4"
3) "5"


이것은 당신이 당신의 목록을 조작하는 유일한 사람일 때 잘 작동합니다. 그러나 다른 사람이 동시에 목록을 조작하는 경우에는 어떻게 될까요?

> RPUSH mykey 0 1 2 3 4 5
(integer) 6
> LRANGE mykey 0 2
1) "0"
2) "1"
3) "2"
> LTRIM mykey 3 -1
OK
> LRANGE mykey 0 99
(empty array)


어 오. B라고 부를 또 다른 소비자는 실행LRANGE mykey 0 2하고 수신0 1 2했습니다. 우리는 그 직후에 같은 명령을 실행했고 같은 결과를 받았습니다. 소비자 B는 이제 LTRIM mykey 3 -1 실행되고 우리도 실행합니다. 이제 목록은 비어 있지만 두 소비자 모두 목록의 처음 3개 항목만 받았습니다. 마지막 3명이 사라졌습니다.

해결책



이 두 트랜잭션 LRANGELTRIM 가 원자성이어야 합니다. 즉, 둘 다 실행하거나 둘 다 실행하지 않습니다. Redis에서는 transactions 을 사용하여 이를 달성할 수 있습니다. 트랜잭션의 모든 명령은 직렬화되고 순차적으로 실행됩니다. Redis 트랜잭션이 실행되는 도중에 다른 클라이언트가 발행한 요청이 처리되는 일은 절대 있을 수 없습니다. 이렇게 하면 명령이 격리된 단일 작업으로 실행됩니다.

Redis CLI를 사용하는 경우 코드를 다음과 같이 변경합니다.

> MULTI
OK
> RPUSH mykey 0 1 2 3 4 5
QUEUED
> LTRIM mykey 3 -1
QUEUED
> EXEC
1) (integer) 6
2) OK


파이썬에서



실제로 CLI를 사용하는 사람은 없기 때문에 Python에서 이 작업을 수행해 보겠습니다. 이 스크립트는 redis-py 의 파이프라인 기능을 사용합니다. 파이프라인은 단일 요청에서 서버에 대한 여러 명령 버퍼링을 지원하는 기본 Redis 클래스의 하위 클래스입니다.

>>> import redis
>>> my_key = 'pop_trim_test'
>>> r = redis.Redis(host='localhost', port=6379)
>>> r.rpush(my_key, *[x for x in range(10)])
>>> pipe = r.pipeline()
>>> pipe.lrange(my_key, 0, 3)
>>> pipe.ltrim(my_key, 4, -1)
>>> pipe.execute()
[[b'0', b'1', b'2', b'3'], True]

pipe.execute() 파이프 명령의 결과를 목록으로 순차적으로 반환합니다. 따라서 우리lrange의 결과가 목록의 첫 번째 항목입니다.

더 나은



애플리케이션이 대기열의 항목을 기다리는 소비자 모델로 작업하는 경우 위 코드는 지속적으로 Redis를 쿼리하기 때문에 문제가 될 수 있습니다. 더 나은 동작은 Redis' blocking pop (BLPOP) function 을 사용하는 것입니다. BLPOP을 사용하면 키가 비어 있으면 다른 클라이언트가 키에 대해 LPUSH 또는 RPUSH 작업을 수행할 때까지 클라이언트가 연결을 차단합니다. 여러 소비자를 처리하는 데 유용한 리소스입니다. 소비자가 BLPOP을 성공적으로 실행한 후 위의 코드를 실행하여 이 작업을 수행할 수 있습니다.

좋은 웹페이지 즐겨찾기