Во-первых, вы всегда должны подробно описывать свои настройки для такого вопроса, поскольку макет памяти зависит от ОС, распределителя памяти, платформы и версии Redis.
В 64-разрядной Linux-коробке с Redis 2.4 набор элементов размером 1 Мбайт с 8 байтами ест 87 МБ.
Это похоже на размер ключей, но любая динамическая структура данных, обеспечивающая эффективный доступ к ее элементам, связана с накладными расходами. Чем меньше ваши предметы, тем больше накладные расходы.
С Redis большие комплекты реализованы с использованием отдельных целых хеш-таблиц. Каждая запись представлена следующая структура:
typedef struct dictEntry {
void *key;
void *val;
struct dictEntry *next;
} dictEntry;
Поскольку нет 24 байта класса поддерживается распределитель памяти (jemalloc), использует 32 байта. В этой структуре, вал установлен в 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 байт, плюс NUL символ, таким образом, размер будет 17 байт на ключи. Следующий класс распределения - 32 байта с jemalloc, поэтому эта структура займет 32 байта.
В целом, каждый элемент будет стоить: 32 + 16 + 32 = 80 байт. Есть 1M от них. Добавьте место для самой хэш-таблицы (содержащей по крайней мере 1M указателей на struct dictEntry), и вы получите результат, который очень близок к 87 МБ, который мы можем измерить на этой платформе.
Оптимизация объема памяти большого набора на самом деле не является тривиальной.Redis выполняет оптимизацию, когда наборы малы (по умолчанию менее 512 элементов), а клавиши - целые. См. Дополнительную информацию here.
Одной из возможных оптимизаций является увеличение параметра set-max-intset-entries и разделение набора на различные части. Например, клавиши элементов можно хэшировать для распределения элементов на разных наборах. Вместо простого myset у вас есть myset: 0, myset: 1, myset: 2 ... myset: n. Для проверки заданного элемента задано значение хеша рассчитывается на ключе, чтобы найти правильный myset: X-запись, а затем эта конкретная запись проверяется. Цель состоит в том, чтобы сохранить размер всех этих наборов ниже параметра set-max-intset-entries, чтобы извлечь выгоду из оптимизации памяти. Конечно, это делает все операции на множестве более сложными, поэтому это действительно компромисс между сложностью и объемом памяти.
Это совершенно неправильно. Наборы в Redis хранятся как хэш-таблицы, а не деревья. Redis не объединяет распределения: в основном он использует отличный универсальный распределитель jemalloc (начиная с версии 2.4). В Redis нет сборки мусора: вместо этого используется подсчет ссылок. –
Спасибо за исправление. В моей защите я только догадывался о внутренней структуре, которую использует Редис, и я нашел ваш подробный ответ выше интересного и освещающего. –