Redis:将随机成员从一个集合移动到另一个集合

时间:2014-11-25 17:22:07

标签: redis

SRANDMEMBER从Set返回一个随机成员。 SPOP从Set中删除随机成员。 SMOVE将指定成员从一个Set移动到另一个Set。

如何在一次拍摄中将随机成员从一个集合移动到另一个集合? 我不想激活一个SRANDMEMBER,等待它的结果,然后调用SMOVE。

我尝试使用EVAL脚本,但它不能与SRANDMEMBER一起使用,发出“在非确定性命令后不允许写入命令”。

操作必须是O(1)和原子。

2 个答案:

答案 0 :(得分:2)

Pre Redis v3.2

Redis可以防止Lu​​a脚本中的随机写入,从而不会破坏复制(脚本也会在从属服务器上进行评估)。也就是说,这是一个伪随机解决方案:

local t = redis.call("SMEMBERS", KEYS[1])
local n = redis.call("SCARD", KEYS[1])
local r = math.random(1, n+1)
redis.call("SMOVE", KEYS[1], KEYS[2], t[r])
return t[r]

注意:

  1. 伪随机 - Redis确保对random的调用实际上是确定性的
  2. 设置大小 - 因为提取了所有set的成员,所以你应该小心不要在biggish上运行它。
  3. v3.2及更高版本

    从3.2开始,您可以通过先验地调用redis.replicate_commands()来更随机。

答案 1 :(得分:0)

我只是在写出解决方案(对于redis v3.2 +,由Itamar Haber的回答暗示。对于那些希望复制粘贴工作而不是实际学习lua的人来说(就像我!)

        redis.replicate_commands()  -- needed since we call SRANDMEMBER below
        local value = redis.call('SRANDMEMBER', KEYS[1])
        if value then
            redis.call('SMOVE', KEYS[1], KEYS[2], value)
        end
        return value

在我的情况下,我正在使用pyredis,所以我写了这个函数:

def redis_smoverand(redis):
    """Register our smoverand() script on the given redis instance.

    smoverand() selects a random member from a set, moves it to another set atomically, and returns
    it. Usage:
    redis_smoverand(redis)('source_set', 'dest_set')

    The script is cached the first time this function is called."""

    if not getattr(redis, '_smoverand', None):
        lua = """
            redis.replicate_commands()  -- needed since we call SRANDMEMBER below
            local value = redis.call('SRANDMEMBER', KEYS[1])
            if value then
                redis.call('SMOVE', KEYS[1], KEYS[2], value)
            end
            return value
        """

        redis._smoverand = redis.register_script(lua)

    return lambda k1, k2: redis._smoverand(keys=(k1, k2), args=())