2012-04-04 3 views
17

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

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

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

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

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

ответ

75

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

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

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

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

Redis предоставляет memory optimizations для некоторых типы данных, но они не охватывают множества строк. Если вам действительно нужно оптимизировать потребление памяти наборами, есть трюки, которые вы можете использовать. Я бы не сделал этого всего за 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.

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

+0

Увлекательный и подробный ответ; Я этого не знал. Спасибо @Didier! –

+0

Хорошо, спасибо, я очень уверен, что это решит мои проблемы. И да, для 160mb это прекрасно, но я ожидаю работать с до 1gb простых данных слова и не хотел, чтобы это увеличилось до 10gb. Еще раз спасибо, оцените подробный ответ. – cwoebker

+2

@ Didier - Отличный ответ! Пара исправлений, хотя a) Записи Hashtable представляют собой один связанный список, а не двойной, служебные данные 24 байта правильны, хотя b) Оболочка объекта Redis не применяется к каждой записи набора/хэша. Он применим только к парам ключ/значение верхнего уровня, так что накладные расходы постоянны. C) Вы можете указать, что zipmap устарел в 2.6/unstable, и этот ziplist делает эквивалентную вещь. –

2

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

Также: Какая версия Redis вам подходит? Взгляните на this blog post - он говорит, что фрагментация частично решена с версии 2.4.

4

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

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

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

У попробовать в случае, если кто-то нуждается код бенчмаркинга, напишите мне

Смежные вопросы