Сколько байтов занято членом в Redis Set - PullRequest
3 голосов
/ 05 января 2012

Я использую Redis как хэш-сет в памяти.После того, как я вставил 8-байтовые ключи 1M (в двоичном виде) в набор, я обнаружил, что значение Redis USED_MEMORY составляет около 100 МБ, что означает, что один элемент занимает 100 байт?почему?

Или как я могу настроить Redis для экономии памяти.

Ответы [ 2 ]

7 голосов
/ 05 января 2012

Во-первых, вы всегда должны подробно описывать настройку для этого типа вопроса, поскольку структура памяти зависит от ОС, распределителя памяти, платформы и версии Redis.

В 64-битном Linux-боксе с Redis 2.4 набор из 1M элементов из 8-байтовых ключей потребляет 87 МБ.

Кажется, это очень много по сравнению с размером ключей, но любая динамическая структура данных, поддерживающая эффективный доступ к ее элементам, требует дополнительных затрат. Чем меньше ваши вещи, тем больше накладные расходы.

В Redis большие наборы реализуются с использованием отдельных хэш-таблиц цепочки. Каждая запись представлена ​​следующей структурой:

typedef struct dictEntry {
    void *key;
    void *val;
    struct dictEntry *next;
} dictEntry;

Поскольку не существует 24-байтового класса, поддерживаемого распределителем памяти (jemalloc), используется 32 байта. В этой структуре значение val равно NULL (это набор), а ключ указывает на объект, определенный следующим образом:

typedef struct redisObject {
    unsigned type:4;
    unsigned storage:2;     /* REDIS_VM_MEMORY or REDIS_VM_SWAPPING */
    unsigned encoding:4;
    unsigned lru:22;        /* lru time (relative to server.lruclock) */
    int refcount;
    void *ptr;
} robj;

Эта структура занимает всего 16 байтов. Он указывает на сами ключевые данные, представленные этой структурой переменной длины:

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

Ключи на 8 байтов, плюс нулевой символ, поэтому размер будет 17 байтов на ключи. Следующий класс выделения - 32 байта с jemalloc, поэтому эта структура займет 32 байта.

В целом каждый элемент будет стоить: 32 + 16 + 32 = 80 байт. Есть 1М от них. Добавьте некоторое пространство для самой хеш-таблицы (содержащей не менее 1М указателей на структуру dictEntry), и вы получите результат, очень близкий к 87 МБ, которые мы можем измерить на этой платформе.

Оптимизация объема памяти большого набора на самом деле не тривиальна. Redis выполняет оптимизацию, когда наборы невелики (по умолчанию менее 512 элементов), а ключи на самом деле целые. Подробнее здесь .

Одной из возможных оптимизаций является увеличение параметра set-max-intset-records и разбиение набора на различные части. Например, ключи элементов могут быть хэшированы для распределения элементов по различным наборам. Вместо просто myset у вас есть myset: 0, myset: 1, myset: 2 ... myset: n. Чтобы проверить, задан ли данный элемент, это набор, по ключу вычисляется хеш-значение, чтобы найти правильную запись myset: X, а затем проверяется эта конкретная запись. Цель состоит в том, чтобы сохранить размер всех этих наборов ниже параметра set-max-intset-records, чтобы воспользоваться преимуществами оптимизации памяти. Конечно, это делает все операции, выполняемые на съемочной площадке, более сложными, поэтому это действительно компромисс между сложностью и объемом памяти.

1 голос
/ 05 января 2012

Без знания базовой структуры каждого члена набора невозможно сказать.Однако, если вы храните ключ / значения, то каждый член хранит ключ и значение (даже если значение пустое, ему все равно нужно хранить ссылку на него).

Для быстрого поиска ключей основная структура, скорее всего, является деревом, что означает, что для хранения левого и правого (или красного / черного) указателя на левый и правый нисходящие узлы в дереве необходимокаждый участник.В 64-битной системе эти указатели имеют 8 байтов каждый.

Для эффективного выделения и освобождения пар ключ / значение каждый узел-член может иметь элементы данных, которые указывают его размер и доступность (выделены, удалены), так что каждый узел-член может быть выделен из пула памяти.и либо мусор собрал, либо пометил как удаленный и использованный повторно.Типичное распределение пула удваивает размер пула каждый раз, когда предыдущий пул заполняется, чтобы минимизировать конфликт кучи, что очень важно для производительности в многопоточных приложениях.Ваше использование памяти объемом 100 миллионов может содержать 50 миллионов неиспользованных (но выделенных) держателей ключей.

Почему вы хотите сэкономить использование памяти?Вы планируете хранить миллиарды хеш-ключей?

...