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

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

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

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

в частности, я хочу описать все типы: string, list, set, zset и hash.

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

3 ответов


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

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

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

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

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

строки

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

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

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

списки

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

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

наборы

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

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

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

Небольшие наборы закодированы в очень эффективном путь.

хэши

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

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

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

Отсортированный Наборы

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

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

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

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

вывод

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

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


большую часть времени вам не нужно понимать базовые структуры данных, используемые Redis. Но немного знаний поможет вам сделать CPU V / S памяти компромиссы. Это также поможет вам эффективно моделировать ваши данные.

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

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

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

1. Струны

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

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

строки являются наиболее универсальной структурой данных, доступной в Redis. Строка все следующее:

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

2. Словарь

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

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

Redis словари реализованы с помощью Хэш-Таблицы. Вместо объясняя реализацию, я просто объясню Redis конкретные вещи:

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

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

3. Дважды Связанные Списки

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

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

Redis использует Пропустить Списки в качестве базовой структуры данных для сортированные наборы. Википедия имеет хорошее введение. Газета Уильяма пью!--150-->пропустить списки: вероятностная альтернатива сбалансированным деревьям более подробную информацию.

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

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

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

5. Список Zip

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

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

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

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

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

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

6. Int Устанавливает

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

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

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

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

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

7. Застежка-Карты

Zip-карты - это словари, сглаженные и сохраненные в списке. Они очень похожи на списки Zip.

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


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

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

2.хеш - коллекция пар ключ-значение

3.lists-коллекция строк в порядке вставки

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

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

строки

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

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

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

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

хэши

хэш Redis-это коллекция пар значений ключей.

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

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

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

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

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

списки

Redis списки действуют как связанные списки.

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

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

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

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

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

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

наборы

наборы Redis-это, ну, наборы.

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

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

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

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

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

Отсортированный Наборы

Redis сортированные наборы-это наборы с пользовательским порядком.

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

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

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

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

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

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