Вставка в структуру перезаписи атомарного индекса в одной команде

80
9

Я создаю структуру перезаписи для сопоставления идентификаторов из пространства имен строк в числовой индекс. Причина, по которой я делаю это, - это то, что идентификаторы строк большие (сотни байтов), их миллионы, и они используются в Redis много разных объектов. Сопоставление сложных идентификаторов строк с более сжатым пространством имен поможет вписываться в соображения компоновки памяти.

Один из способов достижения вышеуказанного - вставить в какую-то структуру и получить обратно автоматически увеличивающееся целое число, если ключ для вставки не существует. Если он существует, просто хотел бы вернуть обратно ранее связанное целое число.

Один неатомный способ выразить выше, через Python, следующий:

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

Операции вставки будут выполняться параллельно, поэтому решение должно быть атомарным.

Я вижу способы выполнить вышеописанное в Redis с несколькими командами внутри транзакции. Что мне интересно, если есть одна команда?

Единая команда не является строгим требованием, но приятной. Вышеизложенное не является необычной задачей, поэтому я подумал, что Redis может добавить прямую поддержку - я все еще изучаю набор команд и, возможно, что-то пропустил.

спросил(а) 2021-01-19T22:44:37+03:00 9 месяцев, 1 неделя назад
1
Решение
79

Вероятно, есть несколько способов сделать то, что вы хотите, поэтому я объясню один из более простых. Во всем этом нет единственной команды Redis, но вы можете собрать те, которые доступны для решения этой задачи.

Мы будем использовать один Redis Hash для хранения всех данных. Хэш, хранящийся в ключе под названием "данные", будет иметь поле для каждого из ваших идентификаторов строк, причем его значение будет числовым. Одно дополнительное поле в Hash, называемое "current_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)

ответил(а) 2021-01-19T22:44:37+03:00 9 месяцев, 1 неделя назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема