Каковы основные структуры данных, используемые для Redis?

Я пытаюсь ответить на два вопроса в окончательном списке:

  • Каковы базовые структуры данных, используемые для Redis?
  • И каковы основные преимущества/недостатки/варианты использования для каждого типа?

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

В частности, я собираюсь описать все типы: string, list, set, zset и hash.

О, я просмотрел эту статью, среди прочего, до сих пор:

Ответ 1

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

Но так как вы спросили, вот базовая реализация каждого типа данных Redis.

  • Строки реализованы с использованием динамической библиотеки строк C, так что мы не платим (асимптотически) за выделение в операциях добавления. Таким образом, O (N) добавляет, например, вместо квадратичного поведения.
  • Списки реализованы со связанными списками.
  • Наборы и Хеши реализованы с помощью хеш-таблиц.
  • Сортированные наборы реализованы с списками пропуска (особый тип сбалансированных деревьев).

Но когда списки, наборы и отсортированные множества малы по количеству элементов и размеру самых больших значений, используется другое, гораздо более компактное кодирование. Эта кодировка отличается для разных типов, но имеет свойство, что это компактный блок данных, который часто заставляет O (N) сканирование для каждой операции. Поскольку мы используем этот формат только для небольших объектов, это не проблема; сканирование маленького O (N) -броса - это кэш, который не обращает внимания, так что практически говоря, это очень быстро, а когда слишком много элементов, кодирование автоматически переключается на нативную кодировку (связанный список, хеш и т.д.).

Но ваш вопрос был не только о внутренних, но и о том, какой тип использовать для выполнения чего?.

Строки

Это базовый тип всех типов. Это один из четырех типов, но также является базовым типом сложных типов, поскольку List представляет собой список строк, Set - это набор строк и т.д.

Строка Redis - хорошая идея во всех очевидных сценариях, в которых вы хотите сохранить HTML-страницу, но также и в том случае, когда вы хотите избежать конвертирования уже закодированных данных. Так, например, если у вас есть JSON или MessagePack, вы можете просто хранить объекты в виде строк. В Redis 2.6 вы можете даже манипулировать этим видом на стороне сервера объектов с помощью сценариев Lua.

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

Списки

Списки хороши, когда вы, вероятно, будете касаться только крайностей списка: около хвоста или около головы. Списки не очень хороши для разбиения на страницы, потому что случайный доступ медленный, O (N). Таким образом, хорошее использование списков - это простые очереди и стеки или обработка элементов в цикле, используя RPOPLPUSH с тем же источником и местом назначения, чтобы "вращать" кольцо элементов.

Списки также хороши, когда мы хотим просто создать ограниченную коллекцию из N элементов, где обычно мы получаем доступ только к верхним или нижним элементам или когда N мало.

Наборы

Установки представляют собой неупорядоченный сбор данных, поэтому они хороши каждый раз, когда у вас есть коллекция элементов, и очень важно очень быстро проверить наличие или размер коллекции. Еще одна интересная вещь в наборах - поддержка поиска или вызова случайных элементов (команды SRANDMEMBER и SPOP).

Наборы также хороши для представления отношений, например, "Что такое друзья пользователя X?" и так далее. Но другие хорошие структуры данных для такого рода вещей сортируются по порядку, как мы увидим.

Устанавливает вспомогательные сложные операции, такие как пересечения, объединения и т.д., поэтому это хорошая структура данных для использования Redis в "вычислительной" манере, когда у вас есть данные, и вы хотите выполнить преобразования этих данных для получения некоторого результата.

Маленькие множества кодируются очень эффективным способом.

Хэш

Хеши - идеальная структура данных для представления объектов, состоящих из полей и значений. Поля хэшей также могут быть атомарно увеличены с использованием HINCRBY. Когда у вас есть объекты, такие как пользователи, сообщения в блогах или какой-либо другой элемент, хеши, скорее всего, подходят, если вы не хотите использовать свою собственную кодировку, такую ​​как JSON или подобное.

Однако имейте в виду, что небольшие хэши очень эффективно кодируются Redis, и вы можете попросить Redis атомарно GET, SET или увеличить отдельные поля очень быстро.

Хэши также могут использоваться для представления связанных структур данных с использованием ссылок. Например, проверьте выполнение комментариев lamernews.com.

Отсортированные наборы

Отсортированные наборы являются единственными другими структурами данных, помимо списков, для поддержки упорядоченных элементов. Вы можете сделать несколько классных вещей с отсортированными наборами. Например, вы можете иметь всевозможные списки Top Something в своем веб-приложении. Лучшие пользователи по количеству баллов, верхние сообщения по просмотрам страниц, что угодно, но один экземпляр Redis будет поддерживать тонны операций вставки и get-top-elements в секунду.

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

Сортированные наборы хороши для очередей приоритетов.

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

Заключение

Надеюсь, что я предоставил некоторую информацию в этом посте, но гораздо лучше загрузить исходный код lamernews из http://github.com/antirez/lamernews и понять, как это работает. Многие структуры данных из Redis используются внутри Lamer News, и есть много подсказок о том, что использовать для решения данной задачи.

Извините за опечатки грамматики, здесь полночь и слишком устал, чтобы просмотреть сообщение;)

Ответ 2

В большинстве случаев вам не нужно понимать базовые структуры данных, используемые Redis. Но немного знаний поможет вам сделать компромисс между CPU v/s. Это также помогает вам правильно моделировать данные.

Внутри Redis использует следующие структуры данных:

  1. строка
  2. Словарь
  3. Дважды связанный список
  4. Пропустить Список
  5. Список Zip
  6. Int Sets
  7. Почтовые карты (устарели в пользу zip-списка с Redis 2.6)

Чтобы найти кодировку, используемую определенным ключом, используйте object encoding <key>.

1. Строки

В Redis строки называются простыми динамическими строками или SDS. Это небольшая оболочка над символом char * которая позволяет хранить длину строки и количество свободных байтов в качестве префикса.

Поскольку длина строки сохраняется, strlen является операцией O (1). Кроме того, поскольку длина известна, строки Redis являются двоичными. Для строки, содержащей нулевой символ, вполне законно.

Строки - это самая универсальная структура данных, доступная в Redis. Строка - это все из следующего:

  1. Строка символов, которая может хранить текст. См. Команды SET и GET.
  2. Байт-массив, который может хранить двоичные данные.
  3. long которые могут хранить цифры. См INCR, ОВЦС, INCRBY и DECRBY команды.
  4. Массив (из chars, ints, longs или любого другого типа данных), который может обеспечить эффективный произвольный доступ. См. Команды SETRANGE и GETRANGE.
  5. Битовый массив, который позволяет вам устанавливать или получать отдельные биты. См. Команды SETBIT и GETBIT.
  6. Блок памяти, который можно использовать для создания других структур данных. Это используется внутренне для создания ziplists и intsets, которые представляют собой компактные, эффективные для памяти структуры данных для небольшого количества элементов. Подробнее об этом ниже.

2. Словарь

Redis использует словарь для следующих целей:

  1. Чтобы сопоставить ключ со своим связанным значением, где значение может быть строкой, хешем, множеством, отсортированным набором или списком.
  2. Чтобы сопоставить ключ с меткой времени истечения срока действия.
  3. Для реализации типов данных Hash, Set и Sorted Set.
  4. Чтобы отобразить команды Redis для функций, которые обрабатывают эти команды.
  5. Чтобы отобразить ключ Redis для списка клиентов, которые заблокированы на этом ключе. См. BLPOP.

Словари Redis реализованы с использованием таблиц Hash. Вместо объяснения реализации я просто объясню конкретные вещи Redis:

  1. Словари используют структуру dictType чтобы расширить поведение хеш-таблицы. Эта структура имеет указатели на функции, и поэтому следующие операции расширяемы: a) хеш-функция, b) сравнение ключей, c) деструктор ключа и d) деструктор значения.
  2. Словари используют murmurhash2. (Раньше они использовали хэш-функцию djb2, с семенем = 5381, но затем хэш-функция была переключена на murmur2. См. Этот вопрос для объяснения алгоритма хеширования djb2.)
  3. Redis использует инкрементное хеширование, также известное как Incremental Resizing. Словарь имеет две таблицы хэшей. Каждый раз, когда словарь затрагивается, одно ведро переносится из первой (меньшей) хэш-таблицы во вторую. Таким образом, Redis предотвращает дорогостоящую операцию изменения размера.

Структура данных Set использует словарь для гарантии отсутствия дубликатов. Sorted Set использует словарь для сопоставления элемента с его счетом, поэтому ZSCORE является операцией O (1).

3. Сопряженные списки

Тип данных list реализуется с использованием Doubly Linked Lists. Реализация Redis - это учебник, основанный на прямом алгоритме. Единственное изменение заключается в том, что Redis сохраняет длину в структуре данных списка. Это гарантирует, что LLEN имеет сложность O (1).

4. Пропустить списки

Redis использует Skip Lists в качестве базовой структуры данных для Sorted Sets. Википедия имеет хорошее представление. Бумага Уильяма Пью Пропустить списки: Вероятностная альтернатива сбалансированным деревьям имеет более подробную информацию.

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

Реализация списков переходов Redis отличается от стандартной реализации следующими способами:

  1. Redis позволяет дублировать баллы. Если два узла имеют одинаковый балл, они сортируются по лексикографическому порядку.
  2. Каждый узел имеет обратный указатель на уровне 0. Это позволяет вам перемещать элементы в обратном порядке.

5. Список почтовых индексов

Список Zip похож на двойной список, за исключением того, что он не использует указатели и сохраняет данные в строке.

Каждый узел в двусвязном списке имеет 3 указателя - один указатель вперед, один обратный указатель и один указатель для ссылки на данные, хранящиеся на этом узле. Указатели требуют памяти (8 байтов в 64-битной системе), поэтому для небольших списков список с двойной связью очень неэффективен.

Список Zip хранит элементы последовательно в Redis String. Каждый элемент имеет небольшой заголовок, в котором хранятся длина и тип данных элемента, смещение к следующему элементу и смещение к предыдущему элементу. Эти смещения заменяют указатели вперед и назад. Поскольку данные хранятся в строке, нам не нужен указатель данных.

Список Zip используется для хранения небольших списков, отсортированных наборов и хэшей. Сортированные наборы сглаживаются в список, такой как [element1, score1, element2, score2, element3, score3] и сохраняются в Zip-списке. Хеши расплющиваются в список, такой как [key1, value1, key2, value2] и т.д.

С Zip-списками у вас есть возможность сделать компромисс между процессором и памятью. Списки Zip являются эффективными с точки зрения памяти, но они используют больше CPU, чем связанный список (или Hash table/Skip List). Поиск элемента в zip-списке - O (n). Вставка нового элемента требует перераспределения памяти. Из-за этого Redis использует эту кодировку только для небольших списков, хэшей и отсортированных наборов. Вы можете настроить это поведение, изменив значения <datatype>-max-ziplist-entries и <datatype>-max-ziplist-value> в redis.conf. Дополнительную информацию см. В разделе "Оптимизация памяти Redis", раздел "Специальная кодировка небольших совокупных типов данных".

Замечания по ziplist.c превосходны, и вы можете полностью понять эту структуру данных, не прочитав код.

6. Наборы Int

Int Sets - причудливое имя для "Сортированные массивы с целыми числами".

В Redis набор обычно выполняется с использованием хеш-таблиц. Для небольших наборов хэш-таблица неэффективна для памяти. Когда набор состоит только из целых чисел, массив часто более эффективен.

Int Set - это отсортированный массив целых чисел. Для поиска элемента используется алгоритм бинарного поиска. Это имеет сложность O (log N). Для добавления новых целых чисел в этот массив может потребоваться перераспределение памяти, которое может стать дорогостоящим для больших целых массивов.

В качестве дополнительной оптимизации памяти Int Sets входят в 3 варианта с разными целыми размерами: 16 бит, 32 бита и 64 бит. Redis достаточно умен, чтобы использовать правильный вариант в зависимости от размера элементов. Когда новый элемент добавляется и превышает текущий размер, Redis автоматически переносит его на следующий размер. Если строка добавлена, Redis автоматически преобразует Int Set в обычный набор на основе хэш-таблицы.

Int Sets - это компромисс между процессором и памятью. Int Sets чрезвычайно эффективны с точки зрения памяти, а для небольших наборов они быстрее, чем хеш-таблица. Но после определенного количества элементов время поиска O (log N) и стоимость перераспределения памяти становятся слишком большими. Основываясь на экспериментах, оптимальный порог для переключения на обычную хеш-таблицу оказался равным 512. Однако вы можете увеличить этот порог (уменьшая его, не имеет смысла) на основе ваших потребностей приложения. См. set-max-intset-entries в файле redis.conf.

7. Почтовые карты

Zip Maps - словари, сплющенные и сохраненные в списке. Они очень похожи на Zip Lists.

Почтовые карты устарели с Redis 2.6, а небольшие хэши хранятся в Zip-списках. Чтобы узнать больше об этой кодировке, обратитесь к комментариям в zipmap.c.

Ответ 3

В Redis хранятся ключи, указывающие на значения. Ключи могут быть любыми двоичными значениями до разумного размера (для чтения и отладки рекомендуется использовать короткие строки ASCII). Значения являются одним из пяти родных типов данных Redis.

1.strings - последовательность двоичных безопасных байтов до 512 МБ

2.hashes - набор пар значений ключа

3.lists - набор строк в порядке размещения строк

4.sets - набор уникальных строк без упорядочения

5.sorted sets - набор уникальных строк, упорядоченных по пользовательскому скорингу

Строка

Строка Redis представляет собой последовательность байтов.

Строки в Redis бинарно безопасны (это означает, что они имеют известную длину, не определенную никакими специальными завершающими символами), поэтому вы можете хранить что-либо до 512 мегабайт в одной строке.

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

Для всех возможных операций над строками см. http://redis.io/commands/#string

Хэш

Redis hash - это набор пар значений ключа.

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

Вы можете думать о хэшах Redis двумя способами: как представление прямого объекта и как способ компактного хранения многих небольших значений.

Прямые представления объектов просты для понимания. Объекты имеют имя (ключ хэша) и набор внутренних ключей со значениями. См. Пример ниже, ну, пример.

Сохранение многих небольших значений с использованием хэша - это умная технология хранения данных Redis. Когда хэш имеет небольшое количество полей (~ 100), Redis оптимизирует эффективность хранения и доступа для всего хеша. Небольшая оптимизация хранения хэшей Redis вызывает интересное поведение: более эффективно иметь 100 хэшей, каждая из которых содержит 100 внутренних ключей и значений, а не 10 000 ключей верхнего уровня, указывающих на строковые значения. Использование хэшей Redis для оптимизации хранения данных таким образом требует дополнительных накладных расходов на программирование для отслеживания, где заканчиваются данные, но если ваше хранилище данных основано на строках, вы можете сэкономить много накладных расходов памяти, используя этот странный трюк.

Для всех возможных операций с хэшами см. хеш-документы

Списки

Redis перечисляет действия, подобные связанным спискам.

Вы можете вставлять, удалять и пересылать списки из головы или хвоста списка.

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

Списки Redis часто используются как очереди производителей/потребителей. Вставьте элементы в список, а затем поместите элементы из списка. Что произойдет, если ваши потребители попытаются выскочить из списка без элементов? Вы можете попросить Redis ждать появления элемента и сразу же вернуть его вам, когда он будет добавлен. Это превращает Redis в систему очереди сообщений/событий/заданий/задач/уведомлений в реальном времени.

Вы можете удаленно удалять элементы из любого конца списка, позволяя любому списку обрабатываться как стек или очередь.

Вы также можете поддерживать списки фиксированной длины (ограниченные коллекции), обрезая список до определенного размера после каждой вставки.

Для всех возможных операций над списками см. списки документов

Наборы

Множества Redis, ну, наборы.

Набор Redis содержит уникальные неупорядоченные строки Redis, где каждая строка существует только один раз для каждого набора. Если вы добавите один и тот же элемент десять раз в набор, он будет отображаться только один раз. Наборы отлично подходят для ленивого обеспечения того, что что-то существует хотя бы один раз, не беспокоясь о дублирующих элементах, накапливающих и истощающих пространство. Вы можете добавить одну и ту же строку столько раз, сколько хотите, не требуя проверки, существует ли она.

Установки быстро проверяются, вставляются и удаляются члены в наборе.

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

У наборов есть постоянный доступ времени для проверки членства (в отличие от списков), и у Redis даже есть удобное удаление случайного элемента и его возврат ( "pop случайный элемент из набора" ) или случайный член, возвращающийся без замены ( "дайте мне 30 случайных чисел, ish unique users" ) или с заменой ( "дайте мне 7 карт, но после каждого выбора верните карту, чтобы ее можно было снова отбирать" ).

Для всех возможных операций над наборами см. устанавливает документы.

Сортированные наборы

Сортированные множества Redis представляют собой наборы с пользовательским упорядочением.

Для простоты вы можете подумать о сортированном наборе как бинарном дереве с уникальными элементами. (Отсортированные наборы Redis на самом деле списки пропуска.) Порядок сортировки элементов определяется каждым счетом элемента.

Сортированные наборы все еще наборы. Элементы могут появляться только один раз в наборе. Элемент, для целей уникальности, определяется его содержимым строки. Вставка элемента "яблоко" со счетом сортировки 3, а затем вставка элемента "яблоко" со счетом 500 сортировки приводит к одному элементу "яблоко" со счетом 500 сортировки в вашем отсортированном наборе. Наборы уникальны только на основе данных, а не на основе пар (Score, Data).

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

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

Вы можете извлекать элементы несколькими способами. Поскольку все сортируется, вы можете попросить элементы, начинающиеся с самых низких оценок. Вы можете запросить элементы, начинающиеся с самых высоких баллов ( "в обратном порядке" ). Вы можете запросить элементы по их сортировке в натуральном или обратном порядке.

Для всех возможных операций с отсортированными наборами см. отсортированные наборы docs.