Хранение данных с помощью memcs
Движок memcs использует однопоточный обработчик транзакций (поток TX), аналогично memtx, и хранит данные в арене memtx, но в отличие от memtx не организует данные в кортежи. Вместо этого данные хранятся по колонкам. Каждому полю формата назначается собственная древовидная структура типа BPS (BPS-вектор), в которой хранятся значения только этого поля. Если тип поля помещается в 32 байта, исходные значения поля хранятся непосредственно в листьях дерева без какого-либо кодирования. Строки хранятся в формате, аналогичном «Arrow Variable-size Binary View Layout», также называемом «German Strings».
Главное преимущество такой организации данных – значительное повышение производительности последовательного сканирования
колоночных данных по сравнению с memtx благодаря локальности кэша процессора. Поэтому в memcs поддерживается специальный C API
для таких колоночных сканирований: см. box_index_arrow_stream() и box_raw_read_view_arrow_stream(). Пиковая
производительность достигается при сканировании встроенных типов полей.
Запрос полных кортежей, как в memtx, также поддерживается, но производительность ниже по сравнению с memtx, поскольку кортеж приходится конструировать в арене среды выполнения (runtime arena) из отдельных значений полей, собранных из каждого дерева колонок.
Другие возможности:
- Точечный поиск;
- Стабильные итераторы;
- Вставка / замена / удаление / обновление;
- Пакетная вставка в формате Arrow;
- Транзакции, включая транзакции между движками с memtx (при
memtx_use_mvcc_engine = false); - Поддержка представления для чтения;
- Вторичные индексы с возможностью указания покрываемых колонок и последовательного сканирования индексированных и покрываемых колонок.
- Колоночная организация данных – данные хранятся по колонкам, что обеспечивает эффективную агрегацию, фильтрацию и сканирование.
- Поддержка Apache Arrow – данные можно экспортировать в формате Arrow без преобразования, что обеспечивает взаимодействие без копирования.
- Кодирование по словарю – снижает использование памяти для строковых колонок с повторяющимися значениями.
- Сжатие LZ4 – сжимает данные колонок для уменьшения объема используемой памяти.
memcs используется как движок хранения для объектов space и создается с помощью функции
box.schema.space.create():
box.schema.create_space('analytics_data', {engine = 'memcs',field_count = 4,format = {{name = 'id', type = 'uint64'},{name = 'event_type', type = 'string', compression = 'lz4'},{name = 'timestamp', type = 'datetime'},{name = 'value', type = 'double', compression = {type = 'lz4', acceleration = 1000}},}})
Движок memcs поддерживает широкий диапазон типов данных, включая:
- Целочисленные типы:
int8,uint8,int16,uint16,int32,uint32,int64,uint64; - Типы с плавающей точкой:
double,float,float32,float64; - Строки:
string; - Decimal:
decimal,decimal32,decimal64,decimal128иdecimal256.
В движке memcs поддерживается кодирование по словарю для строковых колонок. Уникальные строковые значения хранятся в общем словаре, а повторяющиеся значения заменяются небольшими целочисленными идентификаторами. Кодирование по словарю включается с помощью опции index_opts.layout:
local s = box.schema.create_space('test', {engine = 'memcs', format = format, field_count = field_count,})s:create_index('pk', {layout = 'dict'})
Ограничения:
- Поддерживаются только неключевые строковые колонки.
- Максимум
UINT16_MAX(65536) уникальных значений на колонку. - Идентификаторы в словаре используют тип
uint16(2 байта на значение).
Использование памяти: 2 * space_size + dict_size
Память, используемая словарем, включена в статистику space:bsize().
Гарантии ArrowStream:
- Словарь возвращается как
string-view, индексы какuint16 - Все пакеты используют общий словарь, пока не вставлены новые уникальные значения
- Словари возвращаются по ссылке (без копирования), поэтому экспорт ArrowArray выполняется эффективно
- Словари только увеличиваются – ранее созданные пакеты остаются совместимыми
В memcs поддерживается задание макетов колонок на нескольких уровнях. Приоритет следующий (от высшего к низшему):
- Внутри index_opts.covers в определении индекса.
- Внутри index_opts.layout в определении индекса (по умолчанию для nullable-полей).
- Внутри
formatв определении спейса.
-- 1. Внутри coversbox.space.test:create_index('sk', {parts = {'c2', 'c3'},covers = {{'c4', layout = 'plain'},{'c5', layout = 'null_rle'},},})-- 2. Внутри layoutbox.space.test:create_index('sk', {parts = {'c2', 'c3'},covers = {'c4', 'c5'},layout = 'null_rle',})-- 3. Внутри formatbox.space.test:format({{name = "c2", type = "number"},{name = "c3", type = "number"},{name = "c4", type = "number", is_nullable = true, layout = 'null_rle'},{name = "c5", type = "number", is_nullable = true},})
Поддерживаемые макеты:
plain– макет по умолчанию, без кодирования.null_rle– RLE-кодирование для nullable-полей.dict– кодирование по словарю для строковых полей.
По умолчанию значения NULL хранятся явно и занимают такой же объем памяти, как и любое другое допустимое значение колонки (1, 2,
4, 8, 16 или 32 байта в зависимости от точного типа поля). Однако также поддерживается RLE-кодирование NULL-значений с помощью
макета null_rle. Например, в колонке с 90% равномерно распределенных значений NULL RLE-кодирование снижает потребление памяти
примерно в 5 раз.
Макет null_rle можно задать на трех уровнях:
- Внутри
coversв определении индекса (наивысший приоритет). - Внутри
layoutв определении индекса (по умолчанию для nullable-полей). - Внутри
formatпри определении спейса (низший приоритет).
В memcs поддерживается сжатие на уровне колонок с использованием алгоритма LZ4, который обеспечивает баланс между скоростью и коэффициентом сжатия. Подробности о движках сжатия см. в разделе Сжатие столбцов.