Здравствуйте, студенты, на прошлом уроке мы подробно представили базовую структуру хранения данных Redis, а сегодня мы сосредоточимся на пяти основных принципах структуры данных Redis.
Введение в использование пяти структур данных в Redis
Одной из наиболее заметных особенностей Redis является то, что структура данных богаче,"строка, хэш, список, набор, zset, новая структура данных Redis5.0 — поток"
Использование этой части относительно просто, вы можетеОбратитесь к системе официального веб-сайта Redis.Изучайте и используйте, давайте сосредоточимся на базовой конструкции структуры данных Redis.
Команды Redis не чувствительны к регистру, но ключи строго чувствительны к регистру! ! !
"Учитель, эта часть закончена?"
«Я тебе верю, тебе понадобится максимум час, чтобы прочитать все API, давай!»
Redis — строковый объект (строка)
Давайте посмотрим на базовую структуру строкового типа на примере из предыдущего урока.object encoding keyкоманда для просмотра определенной структуры хранилища
Как видно из рисунка выше, разные строки имеют разную внутреннюю структуру, одна — embstr, другая — raw,
«Учитель, что означает embstr? В чем разница между тремя реализациями?»
«Не торопитесь, мы начнем подробно объяснять дальше».
Чтобы максимизировать использование памяти, Redis предоставляет три структуры данных для строковых объектов.
REDIS_ENCODING_INT(long 类型的整数)
REDIS_ENCODING_EMBSTR embstr (编码的简单动态字符串)
REDIS_ENCODING_RAW (简单动态字符串)
Далее, давайте посмотрим на конкретные различия
int
Согласно предыдущему разделу мы знаем, что значение в каждой хеш-таблице будет указывать на объект redisObject в качестве указателя. Этот объект имеет атрибут ptr (тип указателя). Например, указатель хранится в 8 байтах в 64-байтовом массиве. битовая система.Размер хранилища равен ровно одному длинному целочисленному хранилищу.,Итак, чтобы сэкономить место в памяти, если содержимое строки можно преобразовать в длинное, то строка будет преобразована в тип long, объект ptr указывает на тип long, а кодировка установлена в int, так что нет необходимости заново открывать пробел, что является оптимизацией длинного формирования
raw
Если строковый объект содержит строковое значение, а этоДлина строки больше 32 байт, то строковый объект будет использоватьПростая динамическая строка (SDS) для хранения этого строкового значения и установки исходной кодировки объекта.
embstr
Если строковый объект содержит строковое значение, а этоДлина строки меньше или равна 44 байтам, тогда строковый объект будет использовать кодировку embstr.способ сохранить эту строку.
После версии 3.2 это 44 байта, до 39 байт, что также вызвано изменением версии структуры sds.
Как тип embstr хранит строки [Ключевые моменты]
Мы знаем, что основной процессор сначала считывает данные из памяти в строку кэша, Строка кэша в основном занимает 64 байта, из которых redisObject занимает не менее 16 байт (рассчитывается в соответствии с типом атрибута), поэтому, если вы хотите прочитать redisObject, вы обнаружите, что читаются только 16 байтов, а остальные 48 байтов. пространства эквивалентно отходам, поэтому для повышения производительности (в основном, уменьшения количества операций чтения памяти), поэтому после пространства RedisObject открывается непрерывное пространство размером 48 байт, и значение, на которое указывает ptr, хранится в нем. Примечание Здесь хранится строковый тип, а 48 байт соответствуют структуре хранения sdshdr8. а также sdshdr8 требует как минимум 4 байта без хранения данных (один из байтов - это '\0' в конце строки), тогда остается 44 байта, поэтому, если строка в пределах 44 байтов, можно поместить в строку кеша , тем самым уменьшая количество операций ввода-вывода памяти
Преимущества кодировки embstr:
- Кодировка embstr создаст требуемый строковый объектКоличество выделений памяти было уменьшено с двух для необработанного кодирования до одного..
Необработанное кодирование вызовет функцию выделения памяти дважды, чтобы создать структуру redisObject и структуру sdshdr соответственно, в то время как кодирование embstr будет выделять непрерывное пространство, вызывая функцию выделения памяти один раз, и пространство содержит две структуры redisObject и sdshdr по очереди.
-
Освободить закодированный строковый объект embstrНужно только один раз вызвать функцию освобождения памяти, а для освобождения необработанного закодированного строкового объекта требуется два вызова функции освобождения памяти.
-
Поскольку все данные строкового объекта, закодированного с помощью embstr, хранятся в непрерывной части памяти, строковый объект этой кодировки сравнивается с необработанным,Закодированные строковые объекты лучше используют кэширование.
Redis — объект списка (список)
До версии 3.2 использовались структуры ziplist и linkedlist.
Список представляет собой упорядоченную (отсортированную в соответствии с добавленной временной последовательностью) структуру данных. Как правило, мы будем использовать массив или двусвязный список. Двусвязный список фактически будет тратить память из-за наличия передних и задних указателей.
До версии 3.2 в качестве базовой реализации использовались две структуры данных:
- почтовый список
- Двусвязный список
По сравнению с двусвязным списком сжатый список позволяет экономить память, поэтому при создании города-списка сначала будет рассматриваться сжатый список, который при определенных условиях будет преобразован в двусвязный список. Сначала мы поймем, что такое сжатый список.
почтовый список (Создан для экономии памяти)
На следующем рисунке показана структура данных ziplist.
Желтая область используется для представления характеристик списка, зеленая область — это конкретный элемент в списке, а ziplist хранится в смежных блоках памяти.
- zlbytes: указывает количество байтов, занимаемых всем ziplist, обычно используется для перераспределения памяти или вычисления конца списка.
- ZLTAIL: Прибытие к смещению последнего узла, легко найти хвостовой узел напрямую
- zllen: количество узлов списка
Обратите внимание, что zllen хранится в 16 битах, что означает, что максимальная длина составляет 65535, поэтому, если длина превышает это значение, количество элементов списка может быть определено только путем обхода узла.
- entryX: каждый узел в списке
- zlend: Функция состоит в том, чтобы отметить конец списка, занимая один байт
Затем сосредоточьтесь на том, как хранится каждый узел в списке.
typedef struct zlentry {
unsigned int prevrawlensize, prevrawlen; // prevrawlen是前一个节点的长度,prevrawlensize是指prevrawlen的大小,有1字节和5字节两种
unsigned int lensize, len; // len为当前节点长度 lensize为编码len所需的字节大小
unsigned int headersize; // 当前节点的header大小
unsigned char encoding; // 节点的编码方式
unsigned char *p; // 指向节点的指针
} zlentry;
void zipEntry(unsigned char *p, zlentry *e) { // 根据节点指针返回一个enrty
ZIP_DECODE_PREVLEN(p, e->prevrawlensize, e->prevrawlen); // 获取prevlen的值和长度
ZIP_DECODE_LENGTH(p + e->prevrawlensize, e->encoding, e->lensize, e->len); // 获取当前节点的编码方式、长度等
e->headersize = e->prevrawlensize + e->lensize; // 头大小
e->p = p;
}
Полный zlentry состоит из следующих 3 частей:
- prevrawlen: Запись количества байтов памяти, занимаемых предыдущим узлом, по которому удобно найти адрес предыдущего элемента
По рисунку видно, что prevrawlen кодируется с переменной длиной, если длина предыдущего узла меньше 254 байт, то для хранения prevrawlen используется только один байт, если он больше или равен 254, то первый байт помечен как 254, а следующие четыре байта представляют собой длину предыдущего узла
- Lensize, len: запишите количество байтов памяти, занимаемых текущим узлом, и тип хранения содержимого, что удобно для анализа.Конкретное соответствие см. в содержимом в текстовом поле выше.
- содержимое: сохраняет значение текущего узла.
Зная принцип ziplist, давайте взглянем наУсловия преобразования сжатого списка в двусвязный список:
- Если длина добавленного строкового элемента превышает значение по умолчанию 64
- zip содержит больше узлов, чем по умолчанию 512 Эти два условия можно изменить в redis.conf.
list-max-ziplist-value 64
list-max-ziplist-entries 512
Недостатки ziplist
Самая большая уверенность в ziplistпроблема с обновлением цепочки
Потому что в ziplist каждый zlentry хранит количество байтов, занятых предыдущим узлом, и это значение закодировано с переменной длиной. Предположим, есть сжатый список, который содержит e1, e2, e3, e4..., размер узла e1 составляет 253 байта, тогда размер e2.prevrawlen составляет 1 байт, если он находится между e2 и e1 в это время Вставляется новый узел e, а общая закодированная длина e (включая длину e1) составляет 254 байта.В это время e2.prevrawlen необходимо расширить до 5 байтов, если общая длина e2 изменится, e3. prevrawlen снова изменяется.Если изменяется длина хранилища, то e3 также необходимо расширить.... Это повторяется до тех пор, пока длина хвостового узла или prevrawlen определенного узла не сможет вместить изменение предыдущего узла. Каждое из этих расширений требует операции перераспределения пространства. То же самое относится и к удалению узла.Пока предварительный просмотр узла после изменения узла операции может привести к обновлению цепочки и перераспределению памяти.
В худшем случае для обновления цепочки требуется N перераспределений пространства, а наихудшая временная сложность каждого перераспределения пространства составляет O(N), поэтому общая временная сложность обновления цепочки составляет O(N^2).
Исходя из вышеизложенного, недостатки ziplist перевешивают преимущества, поэтомуПосле версии 3.2 в качестве быстрого списка используется другая структура данных.
Обновлен до быстрого списка после версии 3.2 (двухсвязный список)
quicklist был введен после версии 3.2.
В соответствии с вышеизложенным, ziplist вводит частое использование и освобождение памяти, в то время как связанный список также будет тратить память из-за указателей, и каждый узел существует отдельно, что вызовет большую фрагментацию памяти, поэтому объединяя характеристики двух структур, Разработанный quickList .
quickList — это двусвязный список ziplist. Каждый узел использует ziplist для хранения данных.По сути, быстрый список содержит небольшой почтовый список.. Структура выглядит следующим образом:
Из рисунка видно, что каждый ziplist представляет собой очень маленький набор.Если ziplist слишком короткий, то чем больше фрагментов памяти и чем он длиннее, тем сложнее выделить большой блок непрерывного пространства памяти. Диапазон также может быть настроен.
- -5: Размер ziplist на каждом узле quicklist не может превышать 64 Кб.
- -4: размер ziplist на каждом узле быстрого списка не может превышать 32 КБ.
- -3: размер ziplist на каждом узле быстрого списка не может превышать 16 Кб.
- -2: Размер ziplist на каждом узле quicklist не может превышать 8 Кб. (по умолчанию)
- -1: Размер ziplist на каждом узле quicklist не может превышать 4 Кб.
list-max0ziplist-size -2
Значение этого параметра можно посмотреть в конфигурационном файле, значение по умолчанию 8kb является оптимальным (-2 соответствует 8kb, вы можете обратиться к комментариям на рисунке ниже)
Мы знаем, что этот список больше подходит для использования в данных горячих точек. Как правило, данные на обоих концах списка наиболее легко доступны, а частота доступа в середине очень низкая. Если этот сценарий выполняется, список также имеет конфигурация, которая может сжимать промежуточные узлы.
- 0: это специальное значение, означающее отсутствие сжатия. Это значение по умолчанию для Redis.
- 1: Указывает, что один узел на обоих концах списка быстрого доступа не сжат, а узлы в середине сжаты.
- 2: Указывает, что два узла на обоих концах списка быстрого доступа не сжаты, а узлы в середине сжаты.
- и так далее
list-compress-depth 0
Суммировать
В этом разделе в основном объясняется базовая структура объектов строк и списков в Redis. Строка представлена тремя структурами: int, raw и embstr. После версии 3.2 список принимает структуру данных быстрого списка. Мы видим, что это экономит память и улучшает производительность запросов.Эффективность отражает отличный дизайн, который может быть использован в качестве ценного опыта в нашем будущем проектировании и разработке.В следующем разделе мы познакомим вас с базовыми структурами данных трех других хэшей, наборов и zset Redis.