Во-первых, вы всегда должны подробно описывать настройку для этого типа вопроса, поскольку структура памяти зависит от ОС, распределителя памяти, платформы и версии 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, чтобы воспользоваться преимуществами оптимизации памяти. Конечно, это делает все операции, выполняемые на съемочной площадке, более сложными, поэтому это действительно компромисс между сложностью и объемом памяти.