2012-01-05 2 views
3

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

Или как я могу скопировать Redis, чтобы сохранить его использование памяти.

ответ

7

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

1

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

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

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

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

+0

Это совершенно неправильно. Наборы в Redis хранятся как хэш-таблицы, а не деревья. Redis не объединяет распределения: в основном он использует отличный универсальный распределитель jemalloc (начиная с версии 2.4). В Redis нет сборки мусора: вместо этого используется подсчет ссылок. –

+0

Спасибо за исправление. В моей защите я только догадывался о внутренней структуре, которую использует Редис, и я нашел ваш подробный ответ выше интересного и освещающего. –