Redis quickList
краткий список
Объекты списков в Redis До версии 3.2 нижняя кодировка списка была реализована с помощью ziplist и linkedlist, но после версии 3.2 была повторно введена структура данных быстрого списка, а нижний уровень списка был реализован с помощью быстрого списка.
В раннем дизайне, когда длина элементов в объекте-списке относительно мала или их количество относительно невелико, для хранения используется ziplist, а когда длина элементов в объекте-списке относительно велика или их количество относительно большой, вместо него будет использоваться двунаправленный список linkedlist для хранения.
Преимущества и недостатки этих двух способов хранения
- Двусвязный список linkedlist удобен для операций push и pop на обоих концах таблицы, а сложность вставки узлов очень низка, но его накладные расходы на память относительно велики. Во-первых, помимо сохранения данных по каждому узлу, ему также необходимо сохранять два дополнительных указателя, во-вторых, каждый узел двусвязного списка представляет собой отдельный блок памяти, и адреса не являются непрерывными. легко генерировать фрагментацию памяти.
- Ziplist хранится в непрерывном сегменте памяти, поэтому эффективность хранения очень высока. Однако это не способствует операциям модификации, а операции вставки и удаления требуют частого выделения и освобождения памяти. Особенно, когда длина ziplist очень велика, перераспределение может привести к большому количеству копий данных.
Базовая структура
конфигурация параметров
-
list-max-ziplist-size -2
Когда принимается положительное значение, это означает, что длина ziplist на каждом узле быстрого списка ограничена в соответствии с количеством элементов данных. Например, если для этого параметра установлено значение 5, это означает, что ziplist каждого узла быстрого списка содержит не более 5 элементов данных.
Когда принимается отрицательное значение, это означает, что длина ziplist на каждом узле quicklist ограничена в соответствии с количеством занятых байтов. В настоящее время он может принимать только пять значений от -1 до -5, и значение каждого значения следующее:
-5: Размер ziplist на каждом узле quicklist не может превышать 64 Кб. (Примечание: 1 КБ => 1024 байта)
-4: размер ziplist на каждом узле быстрого списка не может превышать 32 КБ.
-3: размер ziplist на каждом узле быстрого списка не может превышать 16 Кб.
-2: Размер ziplist на каждом узле quicklist не может превышать 8 Кб. (-2 — это значение по умолчанию, заданное Redis)
-1: Размер ziplist на каждом узле quicklist не может превышать 4 Кб.
-
list-compress-depth 0
Этот параметр указывает количество несжатых узлов на обоих концах списка быстрого доступа.. Примечание. Количество узлов здесь относится к количеству узлов в двусвязном списке быстрого списка, а не к количеству элементов данных в ziplist. На самом деле, ziplist на узле quicklist, если он сжат, сжимается целиком.
параметр
list-compress-depth
Смысл значения следующий: 0: это специальное значение, означающее отсутствие сжатия. Это значение по умолчанию для Redis.
1: Указывает, что один узел на обоих концах списка быстрого доступа не сжат, а узлы в середине сжаты.
2: Указывает, что два узла на обоих концах списка быстрого доступа не сжаты, а узлы в середине сжаты.
3: Указывает, что 3 узла на обоих концах списка быстрого доступа не сжаты, а узлы в середине сжаты.
-
Так далее и тому подобное…
Поскольку 0 является специальным значением, легко увидеть, что головной и конечный узлы быстрого списка всегда несжаты для быстрого доступа к обоим концам списка.
Redis использует алгоритм сжатия для внутренних узлов быстрого списка.LZF- Алгоритм сжатия без потерь.
Определение структуры данных быстрого списка
typedef struct quicklistNode {
struct quicklistNode *prev;
struct quicklistNode *next;
unsigned char *zl;
unsigned int sz; /* ziplist size in bytes */
unsigned int count : 16; /* count of items in ziplist */
unsigned int encoding : 2; /* RAW==1 or LZF==2 */
unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */
unsigned int recompress : 1; /* was this node previous compressed? */
unsigned int attempted_compress : 1; /* node can't compress; too small */
unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;
typedef struct quicklistLZF {
unsigned int sz; /* LZF size in bytes*/
char compressed[];
} quicklistLZF;
typedef struct quicklist {
quicklistNode *head;
quicklistNode *tail;
unsigned long count; /* total count of all entries in all ziplists */
unsigned int len; /* number of quicklistNodes */
int fill : 16; /* fill factor for individual nodes */
unsigned int compress : 16; /* depth of end nodes not to compress;0=off */
} quicklist;
Структура quicklistNode представляет собой узел быстрого списка, а значение каждого поля следующее:
- prev: Указатель на предыдущий узел связанного списка.
- next: указатель на следующий узел в связанном списке.
- zl: указатель данных. Если данные текущего узла не сжаты, они указывают на структуру ziplist, в противном случае — на структуру quicklistLZF.
- sz: Указывает общий размер ziplist, на который указывает zl (включая
zlbytes
,zltail
,zllen
,zlend
и отдельные элементы данных). Следует отметить, что если ziplist сжат, значение sz по-прежнему равно размеру ziplist до сжатия. - count: Указывает количество элементов данных, содержащихся в ziplist. Это поле всего 16 бит. Позже мы посчитаем, достаточно ли этих 16 бит.
- encoding: Указывает, сжат ли ziplist (и какой алгоритм сжатия использовался). В настоящее время есть только два значения: 2 означает сжатие (и использованиеLZFалгоритм сжатия), 1 означает отсутствие сжатия.
- контейнер: зарезервированное поле. Первоначально он был разработан, чтобы указать, хранит ли узел быстрого списка данные непосредственно, использует ли ziplist для хранения данных или использует другие структуры для хранения данных (используемые в качестве контейнера данных, так называемый контейнер). Однако в текущей реализации это фиксированное значение равное 2, что указывает на использование ziplist в качестве контейнера данных.
- recompress: когда мы используем такую команду, как lindex, для просмотра определенного элемента изначально сжатых данных, нам нужно временно распаковать данные, затем установить recompress=1, чтобы сделать пометку, а затем повторно сжать данные, когда есть возможность.
- tryed_compress: это значение полезно только для автоматических тестовых программ Redis. Нас это не волнует.
- дополнительно: другие поля расширения. Он не используется в текущей реализации Redis.