Redis в 10 раз больше памяти, чем данные

У меня маленький вопрос.

Я пытаюсь сохранить список слов в Redis. Производительность отличная.

Мой подход заключается в создании набора под названием "слова" и добавлении каждого нового слова через "sadd".

Вот проблема при добавлении файла, который составляет 15,9 МБ и содержит около миллиона слов, процесс редис-сервера потребляет 160 МБ оперативной памяти. Почему я использую 10-кратную память, есть ли лучший способ решения этой проблемы?

Заранее спасибо

3 ответа

Решение

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

Набор Redis реализован в виде хэш-таблицы. Это включает:

  • массив указателей, растущих геометрически (степени двух)
  • второй массив может потребоваться, когда активна добавочная перефразировка
  • ячейки списка с одной ссылкой, представляющие записи в хеш-таблице (3 указателя, 24 байта на запись)
  • Оболочки объектов Redis (по одному на значение) (16 байт на запись)
  • сами фактические данные (каждый из них имеет префикс 8 байтов для размера и емкости)

Все вышеприведенные размеры приведены для 64-битной реализации. Учет накладных расходов на распределение памяти приводит к тому, что Redis берет не менее 64 байт на элемент набора (поверх данных) для последней версии Redis, использующей распределитель jemalloc (>= 2,4)

Redis обеспечивает оптимизацию памяти для некоторых типов данных, но они не охватывают наборы строк. Если вам действительно нужно оптимизировать потребление памяти наборами, вы можете использовать некоторые приемы. Я бы не стал делать это только для 160 МБ ОЗУ, но если у вас есть большие данные, вот что вы можете сделать.

Если вам не нужны возможности объединения, пересечения, различия множеств, то вы можете хранить свои слова в хеш-объектах. Преимущество заключается в том, что объекты хеша могут автоматически оптимизироваться с помощью Redis с помощью zipmap, если они достаточно малы. Механизм zipmap был заменен на ziplist в Redis >= 2.6, но идея та же: использование сериализованной структуры данных, которая может поместиться в кэш-память ЦП, чтобы получить как производительность, так и компактный объем памяти.

Чтобы гарантировать, что хеш-объекты достаточно малы, данные могут быть распределены в соответствии с некоторым механизмом хеширования. Предполагая, что вам нужно хранить 1M элементов, добавление слова может быть реализовано следующим образом:

  • хэш по модулю 10000 (сделано на стороне клиента)
  • HMSET слова:[hashnum] [слово] 1

Вместо хранения:

words => set{ hi, hello, greetings, howdy, bonjour, salut, ... }

Вы можете хранить:

words:H1 => map{ hi:1, greetings:1, bonjour:1, ... }
words:H2 => map{ hello:1, howdy:1, salut:1, ... }
...

Чтобы извлечь или проверить существование слова, оно совпадает (хешируйте его и используйте HGET или HEXISTS).

С помощью этой стратегии можно существенно сэкономить память при условии, что модуль хеша выбран в соответствии с конфигурацией zipmap (или ziplist для Redis >= 2.6):

# Hashes are encoded in a special way (much more memory efficient) when they
# have at max a given number of elements, and the biggest element does not
# exceed a given threshold. You can configure this limits with the following
# configuration directives.
hash-max-zipmap-entries 512
hash-max-zipmap-value 64

Осторожно: названия этих параметров изменились с Redis >= 2.6.

Здесь по модулю 10000 для 1M элементов означает 100 элементов на хеш-объекты, что гарантирует, что все они будут храниться в виде zipmaps/ziplists.

Что касается моих экспериментов, лучше хранить ваши данные в хэш-таблице / словаре. лучший пример, который я достиг после большого количества сравнительных тестов, - это хранить в ваших записях хеш-таблицы, которые не превышают 500 ключей.

Я попробовал стандартную строку set/get, для 1 миллиона ключей / значений, размер был 79 МБ. Это очень большой случай, если у вас есть большие цифры, например 100 миллионов, которые будут использовать около 8 ГБ.

Я пытался хешировать для хранения одних и тех же данных, для того же миллиона ключей / значений размер был все меньше и меньше 16 МБ.

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

Вы пытались сохранить базу данных (BGSAVE например), выключение сервера и его восстановление? Из-за фрагментации, когда он возвращается и заполняет свои данные из сохраненного файла RDB, это может занять меньше памяти.

Также: с какой версией Redis вы работаете? Взгляните на этот пост в блоге - он говорит, что фрагментация частично решена с версии 2.4.

Другие вопросы по тегам