在单个命令中插入原子索引重写结构

时间:2017-05-10 18:04:22

标签: redis

我创建了一个重写查找结构,用于将ID从字符串命名空间映射到数字索引。我这样做的原因是字符串ID很大(数百个字节),有数百万个,并且它们在Redis中使用了很多不同的对象。将复杂的字符串ID映射到更简洁的命名空间将有助于适应常驻内存设计注意事项。

实现上述目的的一种方法是插入到某个结构中,如果要插入的键不存在,则接收自动递增的整数。如果确实存在,只需要收回先前关联的整数。

通过Python表达上述内容的一种非原子方式如下:

def get_or_set(d, item):
    if item not in d:
        d[item] = len(d)
    return d[item]

插入操作将是并行的,因此解决方案需要是原子的。

我可以看到在Redis中使用多个命令在事务中执行上述操作的方法。我想知道的是,是否只有一个命令?

单个命令不是严格的要求,而是一个令人愉快的命令。以上并不是一个不寻常的任务,所以我认为Redis可能已经添加了直接的支持 - 我仍在学习命令套件,很容易忽略一些东西。

1 个答案:

答案 0 :(得分:2)

可能有几种方法可以做你想要的,所以我将解释一个更简单的方法。所有这些都没有单一的Redis命令,但你可以组装那些可以解决这个问题的命令。

我们将使用单个Redis Hash来存储所有数据。存储在名为“data”的键中的Hash将为每个字符串id设置一个字段,其值为数字id。 Hash中的一个附加字段,称为“current_id”,假设它不是字符串ID之一,将存储最后一个数字id。

不关心原子性,你会这样做(r是你与Redis的联系):

def get_or_set(item):
    kid = r.hget('data', item)
    if kid is None:
        kid = r.hincrby('current_id', 1)
        r.hset('data', item, kid)
    return kid

要添加原子性,您需要将其迁移到Lua脚本。该脚本将在服务器端以原子方式运行,以便处理任何可能的竞争条件。

SCRIPT = """
    local kid = redis.call('HGET', KEYS[1], ARGV[1])
    if not kid then
      kid = redis.call('HINCRBY', KEYS[1], 'current_id', 1)
      redis.call('HSET', KEYS[1], ARGV[1], kid)
    end
    return kid
"""

SHA = r.script_load(script)

def get_or_set(item):
    return r.evalsha(SHA, 1, 'data', item)