Таблица

Таблица — это реляционная таблица, содержащая набор связанных данных, состоящий из строк и столбцов. Таблицы описывают сущности. Например, статья в блоге может быть представлена таблицей article со столбцами: id, date_create, title, author и т.д. Строки содержат данные, а столбцы определяют типы данных. Например, столбец id не может быть пустым (NOT NULL) и должен содержать только уникальные целочисленные значения. Запись в YQL может выглядеть так:

CREATE TABLE article (
    id Int64 NOT NULL,
    date_create Date,
    author String,
    title String,
    PRIMARY KEY (id)
)

YDB поддерживает создание строковых и колоночных таблиц. Основное их отличие — в области применения и формате хранения данных на жестком диске. Для строковых таблиц данные хранятся последовательно в виде строк, а для колоночных — в виде столбцов. У каждого типа таблиц свое предназначение.

Строковые таблицы

Строковые таблицы хорошо подходят для транзакционных запросов, которые генерируют Online Transaction Processing (OLTP) системы, например, бекэнды сервисов погоды или интернет-магазинов. Строковые таблицы предоставляют эффективный доступ к большому количеству столбцов одновременно. Поиск по строковым таблицам работает очень быстро, за счет использования индексов.

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

Когда выполняется запрос на основе столбца (или столбцов), по которому (которым) создан индекс, СУБД может использовать этот индекс, чтобы быстро найти соответствующие строки, минуя полный перебор всех данных. Например, если у вас есть индекс на столбце "author", и вы ищете все статьи, написанные автором "Gray", СУБД использует индекс для быстрого нахождения всех строк с этой фамилией.

Создать строковую таблицу можно через web-интерфейс YDB, с помощью CLI или SDK. Вне зависимости от способа взаимодействия с YDB стоит помнить об общем правиле создания строковой таблицы: таблица должна иметь минимум одну ключевую колонку, при этом допускается создание таблицы, состоящий только из ключевых колонок.

По умолчанию при создании строковой таблицы все столбцы опциональны и могут иметь значения NULL, такое поведение можно изменить и задать условия NOT NULL для ключевых колонок, которые входят в состав первичного ключа. Первичные ключи уникальны, и строковые таблицы всегда упорядочена по ключу. Это означает, что точечное чтение по ключу, а также диапазонные запросы по ключу или префиксу ключа выполняются эффективно (фактически используя индекс). Допускается создание таблицы, состоящей только из ключевых столбцов. К выбору ключа нужно подходить аккуратно, поэтому рекомендуем ознакомиться со статьей: "Выбор первичного ключа для максимальной производительности".

Партиционирование строковой таблицы

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

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

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

Помимо автоматического разделения предоставляется возможность создать пустую таблицу с предопределенным количеством шардов. При этом можно вручную задать точные границы разделения ключей по шардам или указать равномерное разделение на предопределенное количество шардов. В этом случае границы создадутся по первой компоненте первичного ключа. Равномерное распределение можно указать для таблиц, у которых первая компонента первичного ключа — целое число Uint64 или Uint32.

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

Характерное время операции разделения или объединения — порядка 500 мс. На это время вовлеченные в операцию данные становятся кратковременно недоступны для чтения и записи. Специализированные методы-обертки в YDB SDK автоматически выполняют повторные попытки при получении информации о том, что шард находится в состоянии разделения или объединения, не поднимая ее до уровня приложения. Стоит отметить, что если система по каким-то причинам перегружена (например, из-за общей нехватки CPU или пропускной способности выделенных базе дисковых ресурсов) то операции разделения и объединения могут длиться дольше.

В схеме данных определяются следующие параметры партиционирования таблицы:

AUTO_PARTITIONING_BY_SIZE

  • Тип: Enum (ENABLED, DISABLED).
  • Значение по умолчанию: ENABLED.

Режим автоматического партиционирования по размеру партиции. Если размер партиции превысил значение, заданное параметром AUTO_PARTITIONING_PARTITION_SIZE_MB, то она встает в очередь на разделение (split). Если суммарный размер двух или более соседних партиции меньше 50% от значения параметра AUTO_PARTITIONING_PARTITION_SIZE_MB, то они встают в очередь на объединение (merge).

AUTO_PARTITIONING_BY_LOAD

  • Тип: Enum (ENABLED, DISABLED).
  • Значение по умолчанию: DISABLED.

Режим автоматического партиционирования по нагрузке. Если в течение нескольких десятков секунд шард потребляет более 50% CPU, то он ставится в очередь на разделение (split). Если в течение часа суммарная нагрузка на два или более соседних шарда утилизировала менее 35% одного ядра CPU, то они ставятся в очередь на объединение (merge).

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

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

AUTO_PARTITIONING_PARTITION_SIZE_MB

  • Тип: Uint64.
  • Значение по умолчанию: 2000 MB ( 2 ГБ ).

Порог размера партиции в мегабайтах, при превышении которого шард будет разделен, имеет значение при включенном режиме AUTO_PARTITIONING_BY_SIZE.

AUTO_PARTITIONING_MIN_PARTITIONS_COUNT

  • Тип: Uint64.
  • Значение по умолчанию: 1.

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

AUTO_PARTITIONING_MAX_PARTITIONS_COUNT

  • Тип: Uint64.
  • Значение по умолчанию: 50.

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

UNIFORM_PARTITIONS

  • Тип: Uint64.
  • Значение по умолчанию: не применимо.

Количество партиций для равномерного начального разделения таблицы. Первая колонка первичного ключа должна иметь тип Uint64 или Uint32. Созданная таблица сразу будет разделена на указанное количество партиций.

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

PARTITION_AT_KEYS

  • Тип: Expression.
  • Значение по умолчанию: не применимо.

Граничные значения ключей для начального разделения на партиции. Представляется списком граничных значений, разделенных запятыми и обрамленными в скобки. Каждое граничное значение может быть либо набором значений ключевых колонок (также разделенных запятыми и обрамленными в скобки), либо единичным значением, если указываются только значения первой ключевой колонки. Примеры: (100, 1000), ((100, "abc"), (1000, "cde")).

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

Чтение с реплик

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

Применение чтения с фолловеров дает следующие возможности:

  • Обслуживать клиентов, критичных к минимальным задержкам, недостижимым иными способами в мульти-ДЦ кластере. Достигается за счет приближения точки выполнения запроса к точке задания запроса, что отсекает задержку на меж-ДЦ пересылки. В результате, сохраняя все гарантии мульти-ДЦ кластера по надежности хранения, отвечать на точечные читающие запросы за единицы миллисекунд.
  • Обслуживать читающие запросов с фолловеров без влияния на модифицирующие запросы, выполняющиеся на шарде. Это может быть полезно как для изоляции разных сценариев, так и для увеличения пропускной способности партиции.
  • Продолжать обслуживание при переездах лидера партиции (как штатной при балансировке, так и при сбоях). Позволяет переживать процессы в кластере без влияния на читающих клиентов.
  • В целом повышать предел производительности чтения шардов, если множество читающих запросов попадают на одни и те же ключи.

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

Имя параметра Описание Тип Допустимые значения Возможность
изменения
Возможность
сброса
READ_REPLICAS_SETTINGS PER_AZ означает использование указанного количества реплик в каждом AZ, ANY_AZ — во всех AZ суммарно. String "PER_AZ:<count>", "ANY_AZ:<count>" где <count> — число реплик Да Нет

Внутреннее состояние каждого из фолловеров восстанавливается в точности по состоянию лидера и полностью консистентно.

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

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

Автоматическое удаление устаревших данных (TTL)

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

Имя параметра Тип Допустимые значения Возможность
изменения
Возможность
сброса
TTL Expression Interval("<literal>") ON <column> [AS <unit>] Да Да

Где <unit> — единица измерения, указывается только для колонок с числовым типом:

  • SECONDS;
  • MILLISECONDS;
  • MICROSECONDS;
  • NANOSECONDS.

Подробнее об удалении устаревших данных читайте в разделе Time to Live (TTL).

Переименование

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

Операции выполняются изолированно, внешний процесс видит только два состояния таблицы: до и после выполнения операции. Это важно, например, при замене таблицы — данные заменяемой таблицы удаляются в той же транзакции, в которой заменяющая таблица переименовывается. Во время выполнения операции замены возможны ошибки запросов к заменяемой таблице с retryable-статусами.

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

Фильтр Блума

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

Имя параметра Тип Допустимые значения Возможность
изменения
Возможность
сброса
KEY_BLOOM_FILTER Enum ENABLED, DISABLED Да Нет

Группы колонок

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

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

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

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

Для группы колонок устанавливаются атрибуты, влияющие на хранение данных:

  • используемый тип устройств хранения данных (SSD или HDD, доступность типов зависит от конфигурации кластера YDB);
  • режим сжатия данных (без сжатия или сжатие алгоритмом LZ4).

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

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

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

Пользовательские атрибуты

Пользовательские атрибуты позволяют добавлять произвольную информацию к метаданным таблицы. Эта информация не интерпретируется сервером, однако может быть интерпретирована клиентом БД (человеком или, чаще, программой).

Атрибуты задаются в виде пары ключ-значение. Ключом и значением атрибута может быть только строка или тип, который может быть представлен строкой (например, используя base64-кодирование).

Ключи и значения пользовательских атрибутов имеют следующие ограничения:

  • длина ключа — 1–100 байт;
  • длина значения — 1–4096 байт;
  • максимальный общий размер атрибутов (сумма длин всех ключей и значений) — 10240 байт.

О том, как добавить, изменить, получить текущие значения или удалить атрибуты читайте в статье Пользовательские атрибуты таблиц.

Колоночные таблицы

Важно

Колоночные таблицы YDB доступны в режиме Preview.

Колоночные таблицы YDB хранят данные каждого столбца отдельно (независимо) от других столбцов. Такой принцип хранения данных оптимизирован для работы с Online Analytical Processing-нагрузками (OLAP), так как при выполнении запроса считываются только те столбцы, которые непосредственно участвуют в запросе. Еще один плюс такого подхода — высокая степень сжатия данных, так как в столбцах зачастую хранятся повторяющиеся или близкие данные. Минусом является то, что выполнение операций над строками становится более затратным.

На данный момент основной сценарий использования колоночных таблиц YDB — запись данных с возрастающим первичным ключом (например, время события), анализ этих данных и удаление устаревших данных по TTL. Оптимальным способом добавления данных в колоночные таблицы YDB является пакетная запись, выполняемая блоками в единицы МБ. Вставка пакетов данных производится атомарно: данные будут записаны или во все партиции, или ни в одну.

В большинстве случаев работа с колоночными таблицами YDB аналогична работе со строковыми, но есть и отличия:

  • В качестве первичного ключа можно использовать только NOT NULL колонки.
  • Данные партицируются не по первичному ключу, а по Hash от колонок партицирования.
  • В колоночных таблицах поддерживается ограниченный набор типов данных:
    • Доступно и в первичном ключе и в остальных колонках: Date, Datetime, Timestamp, Int32, Int64, Uint8, Uint16, Uint32, Uint64, Utf8, String;
    • Доступно только в колонках, не входящих в первичный ключ: Bool, Decimal, Double, Float, Int8, Int16, Interval, JsonDocument, Json, Uuid, Yson.

Повторим создание таблицы article, на этот раз в колоночной форме, с помощью следующей YQL-команды:

CREATE TABLE article_column_table (
    id Int64 NOT NULL,
    author String,
    title String,
    PRIMARY KEY (id)
)
WITH (STORE = COLUMN);

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

  • Чтение с реплик.
  • Вторичные индексы.
  • Фильтры Блума.
  • Change Data Capture.
  • Переименование таблиц.
  • Пользовательские атрибуты таблиц.
  • Изменение списка колонок данных.
  • Добавление данных в колоночные таблицы с помощью SQL-оператора INSERT.
  • Удаление данных из колоночных таблиц с помощью SQL-оператора DELETE. Фактически, удаление возможно только по истечению TTL времени хранения данных.

Партицирование колоночной таблицы

В отличие от строковых таблиц YDB, колоночные таблицы партицируют данные не по первичным ключам, а по специально выделенным ключам — ключам партицирования. Ключи партицирования являются подмножеством первичных ключей таблицы.

В отличие от партицирования данных в строковых таблицах YDB, партицирование данных для колоночных таблиц выполняется не по значениям ключей, а по hash-значениям от ключей, что позволяет равномерно распределить данные во все существующие партиции. Такое партицирование позволяет избежать хотспотов при вставке и ускоряет аналитические запросы, обрабатывающие (считывающие) большие объемы данных.

Выбор ключей партицирования существенно влияет на производительность колоночных таблиц. Подробнее смотрите: "Выбор первичного ключа для максимальной производительности колоночных таблиц".

Для управления партицированием данных используется дополнительный параметр партиционирования: AUTO_PARTITIONING_MIN_PARTITIONS_COUNT. Остальные параметры партицирования для колоночных таблиц игнорируются.

AUTO_PARTITIONING_MIN_PARTITIONS_COUNT определяет минимальное физическое количество партиций для хранения данных.

Тип: Uint64.

Значение по умолчанию: 64.

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

Предыдущая
Следующая