原子地从Redis数据结构中弹出多个值?

Pav*_* S. 31 time-complexity atomicity redis data-structures

是否有Redis数据结构,它允许弹出(获取+删除)多个元素的原子操作,它包含多个元素?

有众所周知的SPOP或RPOP,但它们总是返回单个值.因此,当我需要来自set/list的前N个值时,我需要调用命令N次,这很昂贵.假设集/列表包含数百万个项目.是否有类似的东西SPOPM "setName" 1000,它会返回并从集合中删除1000个随机项目,或者RPOPM "listName" 1000从列表中返回1000个最右边的项目?

我知道有像SRANDMEMBER和LRANGE这样的命令,但它们不会从数据结构中删除项目.它们可以单独删除.但是,如果有更多客户端从同一数据结构中读取,则可以多次读取一些项目,并且可以在不读取的情况下删除一些项目!因此,原子性是我的问题所在.

此外,如果这种操作的时间复杂性更昂贵,我也没关系.我怀疑它会比发布N(比如上一个例子中的1000,N)单独请求Redis服务器更昂贵.

我也知道单独的交易支持.但是,来自Redis docs的这句话不鼓励我将它用于修改集合的并行进程(从中破坏性地读取):
当使用WATCH时,EXEC只有在未修改被监视的键​​时才会执行命令,允许进行检查和设置机制.

Eli*_*Eli 18

使用LRANGELTRIM流水线.管道将作为一个原子事务运行.你担心上述有关WATCH,EXEC因为你正在运行将不适用在这里LRANGE,并LTRIM为他们之间没有来的能力从任何其他客户任何其他交易一个交易.试试看.

  • redis管道是否保证原子能?我想你的意思是redis [交易](http://redis.io/topics/transactions). (2认同)

tho*_*nic 12

要通过列表集合的完整示例扩展Eli的响应,使用lrangeltrimbuiltins而不是Lua:

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"
Run Code Online (Sandbox Code Playgroud)

如果你想使操作原子,你会包裹lrange和LTRIM multiexec命令.

另外,作为其他地方所指出的,你应该ltrim返回的项目数量没有项目的数量,你提出的要求.例如,如果你做了lrange a 0 99,但有50个项目,你会ltrim a 50 -1不会ltrim a 100 -1.

要实现队列语义而不是堆栈,请替换lpushrpush.

  • 你不能在`multi`和`exec`中使用`lrange`和`ltrim`,因为redis不支持内部事务依赖 (2认同)

Ale*_*ino 7

从Redis 3.2开始,该命令SPOP有一个[count]参数,用于从集合中检索多个元素.

请参见http://redis.io/commands/spop#count-argument-extension

  • 由于OP要求set*或list*和`SPOP`仅适用于set,我在列表中添加了一个完整的示例:http://stackoverflow.com/a/43130793/213983 (2认同)

rad*_*tek 7

这是一个可以使用redis-py管道实现此目的的Python代码片段:

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
Run Code Online (Sandbox Code Playgroud)

我想我可以只做一个 for 循环,pop但这不会有效,即使使用管道,特别是如果列表队列小于prefetch_count. 如果您想查看,我在这里实现了完整的 RedisQueue 类。希望能帮助到你!