Хранение целых чисел в упорядоченном наборе redis?

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

От игры с онлайн-REPL я заметил, что два следующих идентичны

zadd myset 1.0 "123"

zadd myset 1.0 123

Это означает, что даже если я знаю, что хочу сохранить целое число, оно должно быть задано как строка. Я заметил из документации, что ключи просто хранятся как char*, а команды, такие как SETBIT, указывают, что Redis не прочь обрабатывать строки как bytestrings в клиенте. Это указывает на несколько более эффективный способ хранения unsigned long, чем как их строковое представление.

Каков наилучший способ хранения unsigned long в отсортированных наборах?

Ответы

Ответ 1

Спасибо Андре за его ответ. Вот мои выводы.

Сохранение ints напрямую

Ключи Redis должны быть строками. Если вы хотите передать целое число, это должна быть какая-то строка. Для небольших, четко определенных наборов значений, Redis будет анализировать строку в целое число, если оно одно. Я предполагаю, что он будет использовать этот int для адаптации своей хэш-функции (или даже статически измеряет хэш-таблицу на основе значения). Это работает для небольших значений (примерами являются значения по умолчанию для 64 записей значения до 512). Я буду испытывать большие значения во время моего расследования.

http://redis.io/topics/memory-optimization

Сохранение в виде строк

Альтернативой является раздавливание целого числа, чтобы оно выглядело как строка.

Похоже, что в качестве ключа можно использовать любую байтовую строку.

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

Используя Python для моего тестирования, я смог создать значения с помощью struct.pack. long long весит 8 байт, что довольно велико. Учитывая распределение целочисленных значений, я обнаружил, что на самом деле может быть полезно хранить строки, особенно когда они закодированы в шестнадцатеричном формате.

Поскольку строки перерисовывания являются "Pascal-style":

struct sdshdr {
    long len;
    long free;
    char buf[];
};

и учитывая, что мы можем хранить что-нибудь там, я сделал немного дополнительного Python, чтобы закодировать тип в кратчайший возможный тип:

def do_pack(prefix, number):
    """
    Pack the number into the best possible string. With a prefix char.
    """ 

    # char
    if number < (1 << 8*1):
        return pack("!cB", prefix, number)

    # ushort
    elif number < (1 << 8*2):
        return pack("!cH", prefix, number)

    # uint
    elif number < (1 << 8*4):
        return pack("!cI", prefix, number)

    # ulonglong
    elif number < (1 << 8*8):
        return pack("!cQ", prefix, number)

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

Данные, с которыми я работал, были 200000 zsets consecutive integer => (weight, random integer) × 100, плюс некоторый инвертированный индекс (основанный на случайных данных). dbsize дает 1 200 001 ключей.

Конечная память сервера: 1,28 ГБ оперативной памяти, 1.32 виртуальная. Различные настройки имели значение не более 10 мегабайт в любом случае.

Итак, мой вывод:

Не докучайте кодировку в типы данных фиксированного размера. Просто сохраните целое число как строку, в шестнадцатеричном формате, если хотите. Это не будет иметь столь большой разницы.

Литература:

http://docs.python.org/library/struct.html

http://redis.io/topics/internals-sds

Ответ 2

Я не уверен в этом ответе, это скорее предложение, чем что-либо еще. Я бы попробовал и посмотрел, работает ли он.

Насколько я могу судить, Redis поддерживает только строки UTF-8.

Я бы предложил захватить небольшое представление вашего длинного целого и поместить его соответствующим образом, чтобы заполнить ближайший байт. Кодируйте каждый набор из 8 байтов в строку UTF-8 (заканчивая строкой 8x * utf8_char *) и сохраняйте ее в Redis. Тот факт, что они являются неподписанными, означает, что вам не нужен этот первый бит, но если вы это сделали, вы можете добавить флаг в строку.

После извлечения данных вы должны помнить, что каждый символ будет заменен на 8 байт, поскольку UTF-8 будет использовать меньше байтов для представления, если символ может быть сохранен с меньшим количеством байтов.

Конечный результат состоит в том, что вы сохраняете максимум 8 x 8 байтовых символов вместо (возможно) не более 64 x 8 байтовых символов.