TCS Documentation portal logo
Помощь
Обновлена 15 июля 2026 г. в 12:02

Руководство пользователя

Аутентификация

Аутентификация пользователей в TCS производится с помощью компонента CIAM.

Чтение данных

Для обращения к данным в хранилище Tarantool Column Store используются SQL-запросы.

TCS поддерживает следующие способы обращения к данным:

Поддерживаемые операторы и функции SQL описаны в Справочнике по SQL.

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

При настроенном охлаждении данных TCS позволяет выбирать режим чтения для "горячих" и "холодных" данных.

SELECT-запросы через HTTP

Для выполнения запроса SELECT воспользуйтесь одним из двух интерфейсов: SQL или HTTP.

  • SQL:

    Отправьте SQL-запрос через SQL-драйвер.

    Пример запроса на чтение всех объектов таблицы:

    SELECT * FROM users
  • HTTP:

    Отправьте POST-запрос на HTTP-адрес /sql.

    Пример запроса на чтение всех объектов таблицы:

    curl -u tcs:tcs -d 'SELECT * FROM users' http://localhost:7777/sql

    Пример запроса на чтение с фильтром:

    curl -u tcs:tcs -d 'SELECT * FROM users WHERE id=1' http://localhost:7777/sql

    Пример чтения из схемы по умолчанию tcs.public:

    curl -u tcs:tcs -d 'SELECT * FROM users WHERE id=1' http://localhost:7777/sql

Аналитические расчеты

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

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

TCS поддерживает два вида аналитических расчетов:

  • перманентные (хранятся в базе данных);
  • временные (работают только в пределах сессии).

Перманентные аналитические расчеты

Перманентные аналитические расчеты являются хранимыми объектами: чтобы выполнить расчет, нужно сначала его создать и загрузить в TCS. После этого он станет доступен для вызова через HTTP API по имени. Это позволяет уменьшить накладные расходы, возникающие при отправке SQL-запросов через SQL/HTTP API. Среди таких расходов: сериализация и десериализация, передача по сети, разбор и составление плана запроса. Таким образом, аналитические расчеты – оптимальный механизм для ресурсоемких запросов, повторяющихся многократно.

Перманентные аналитические расчеты специфичны для TCS. Они создаются и вызываются только через SQL. Для этого используется код запуска вида PREPARE my_func(INT) AS <query> и последующий вызов через EXECUTE.

Подробнее о работе с этим видом расчетов см. ниже.

Временные аналитические расчеты

Временные аналитические расчеты представляют собой переменную-объект в языке программирования. Они создаются и вызываются только через библиотечные API с помощью кода вызова вида my_db.Prepare(sql).

Подробнее о работе с этим видом расчетов см. Работа с аналитическими расчетами через JDBC/ADBC.

Ограничения при работе с аналитическими расчетами

Аналитические расчеты разных видов можно комбинировать, но с ограничениями:

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

В качестве аналитического расчета используется один подготовленный оператор SQL (prepared statement).

Созданные аналитические расчеты нельзя изменить. Вместо этого удалите существующий расчет и создайте заново.

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

Создание аналитических расчетов

Чтобы создать аналитический расчет, воспользуйтесь одним из двух интерфейсов: SQL или HTTP.

  • SQL:

    Отправьте SQL-запрос через SQL-драйвер.

    Запрос должен содержать SQL-оператор PREPARE, создающий подготовленное SQL-выражение (prepared statement)

    Пример: расчет количества записей в таблице.

    PREPARE plan(INT) AS SELECT count(*) FROM users WHERE age > $1
  • HTTP:

    Отправьте POST-запрос на HTTP-адрес /sql. В качестве значения используйте SQL-оператор PREPARE, создающий подготовленное SQL-выражение (prepared statement).

    Пример: расчет количества записей в таблице.

    curl -u tcs:tcs -d 'PREPARE plan(INT) AS SELECT count(*) FROM users WHERE age > $1' http://localhost:7777/sql

В качестве параметров для подготовленных SQL-выражений используйте типы данных PostgreSQL.

Выполнение аналитических расчетов

Чтобы выполнить существующий в TCS расчет, воспользуйтесь одним из двух интерфейсов: SQL или HTTP.

  • SQL:

    Отправьте SQL-запрос с EXECUTE через SQL-драйвер.

    Пример:

    EXECUTE plan(0)
  • HTTP:

    Отправьте POST-запрос на HTTP-адрес /sql. JSON-тело запроса должно содержать инструкцию EXECUTE, имя и входные параметры расчета.

    Пример:

    curl -u tcs:tcs -d 'EXECUTE plan(0)' http://localhost:7777/sql

    В ответе TCS отправляет результаты выполнения в следующих секциях:

    • success – результаты успешно выполненных запросов;
    • fail – информация о запросах, которые не удалось выполнить;
    • timings – время, потраченное на разные этапы обработки запроса;
    • plans – вывод EXPLAIN ANALYZE для планов, указанных в запросе.

(queries-computation-manage)

Управление аналитическими расчетами

Чтобы получить список аналитических расчетов и их статусов, отправьте запрос SELECT, содержащий функцию tcs_prepareds.

Пример запроса:

SELECT * FROM tcs_prepareds()

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

Пример ответа:

name              query                                 depends_on  status0 my_prepared     PREPARE my_prepared A   S SELECT 1    None        Ready

Статус Ready указывает на то, что аналитический расчет готов. Если аналитический расчет не готов (планируется к выполнению), его статус будет Planning.

Чтобы просмотреть текст конкретного аналитического расчета, отправьте запрос SELECT, содержащий функцию tcs_prepareds и имя расчета.

Пример: просмотр текста существующего аналитического расчета my_prepared.

SELECT * FROM tcs_prepareds() WHERE name = my_prepared

Представления для чтения (read views)

TCS предполагает работу под постоянной транзакционной нагрузкой на запись. Для консистентности чтения данных в таких условиях TCS использует представления для чтения (read view) – снимки хранилища данных в конкретный момент времени.

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

Также есть очередь из используемых в данный момент представлений, из которых еще выполняется чтение в проходящих обработку запросах SQL и прочих.

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

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

См. подробнее:

Нематериализованные SQL-представления (non-materialized views in SQL)

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

Создание нематериализованных SQL-представлений

Чтобы создать нематериализованное SQL-представление, отправьте SQL-запрос, содержащий SQL-оператор CREATE VIEW.

Пример: SQL-представление, содержащее список имен всех совершеннолетних лиц в таблице.

CREATE VIEW adults AS SELECT name FROM users WHERE age >= 18;

Использование нематериализованных SQL-представлений

После создания нематериализованное SQL-представление становится доступно:

  • для запросов на чтение,
  • для аналитических расчетов,
  • для использования в других нематериализованных SQL-представлениях; это означает, что в TCS можно создавать вложенные (иерархические) нематериализованные SQL-представления.

Пример: обращение к SQL-представлению adults из SELECT-запроса.

SELECT salary FROM users WHERE name IS IN (SELECT name FROM adults)

Управление нематериализованными SQL-представлениями

Чтобы просмотреть все доступные нематериализованные SQL-представления, отправьте запрос SELECT, содержащий функцию tcs_views:

SELECT * FROM tcs_views()

Чтобы просмотреть текст конкретного SQL-представления, отправьте запрос SELECT, содержащий функцию tcs_views и имя представления.

Пример: просмотр текста существующего SQL-представления adults.

SELECT * FROM tcs_views() WHERE name = adults

Чтобы заместить существующее SQL-представление, отправьте SQL-запрос, содержащий SQL-оператор CREATE OR REPLACE VIEW.

Пример: замещение существующего SQL-представления adults.

CREATE OR REPLACE VIEW adults AS SELECT name FROM users WHERE age >= 16

Чтобы удалить SQL-представление, отправьте SQL-запрос, содержащий SQL-оператор DROP VIEW и имя представления.

Пример: удаление существующего SQL-представления adults.

DROP VIEW adults

Оптимизация запросов на чтение шардированных данных

В TCS предусмотрены правила оптимизации для запросов на чтение шардированных данных:

  • RemotePushDown
  • LocalizedPrepared

Правило RemotePushDown

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

Далее рассмотрим на примерах, как это правило оптимизации влияет на обработку шардированных запросов на чтение. Предположим, что у нас есть шардированная таблица t(a, b) и 3 шарда.

Простое полное сканирование (full scan)

Рассмотрим простой запрос:

SELECT * FROM t

Запросив EXPLAIN на экземпляре Scheduler, можно увидеть физический план следующего вида:

RemoteExec: table=tcs.public.t, partitioning=Hash([a@0], 3), filter=None  UnionExec    TarantoolExec: table=tcs.public.t fields=[a, b], ...    BufExec: table=tcs.public.t fields=[a, b], ...

В этом плане есть узел RemoteExec, который отправляет нижележащий план на шарды для последующего исполнения. Тот план, который он отправляет, назовем удаленным планом. В нашем примере удаленный план – это план с корневым узлом UnionExec.

В описании RemoteExec мы видим следующее:

  • есть соответствие между этим узлом и таблицей t (table=);
  • партицирование данных производится по хэшу колонки a (согласно схеме шардирования);
  • используется пустой фильтр.

В нашем примере таблица будет просканирована на 3 шардах, а все записи отправлены на экземпляр Scheduler и там объединены.

Одношардовый перебор

Рассмотрим более сложный запрос, где для выполнения не обязательно читать данные со всех шардов:

SELECT * FROM t WHERE a = 1

Здесь мы получаем физический план следующего вида:

RemoteExec: table=tcs.public.t, partitioning=Hash([a@0], 1), filter=tcs.public.t.a=1  CoalesceBatchesExec: target_batch_size=8192    FilterExec: a@0 = 1      UnionExec        TarantoolExec: table=tcs.public.t fields=[a, b], ...        BufExec: table=tcs.public.t fields=[a, b], ...

В описании RemoteExec мы видим следующее:

  • весь план проталкивается внутрь (push down) через RemoteExec;
  • фильтрация выполняется полностью в рамках одного шарда, а по сети пересылаются уже отфильтрованные данные.

Здесь правило RemotePushDown пытается поднять RemoteExec как можно выше.

Для того чтобы посмотреть, как отработало то или иное правило оптимизации, можно использовать запрос EXPLAIN VERBOSE. Он возвращает состояния плана после каждой изменившей его оптимизации. Например, в нашем случае можно видеть, что первоначальный физический план имеет такой вид:

FilterExec: a@0 = 1  RemoteExec: table=tcs.public.t, partitioning=Hash([a@0], 1), filter=tcs.public.t.a=1    UnionExec      TarantoolExec: table=tcs.public.t fields=[a, b], ...      BufExec: table=tcs.public.t fields=[a, b], ...

Здесь фильтрация осуществляется после сбора данных с шардов, а после прохода правила оптимизации RemotePushDown фильтр проталкивается вниз через RemoteExec, вследствие чего запрос становится лучше локализован в рамках шарда.

Исполнение частичной агрегации на шардах

Правило RemotePushDown также может обеспечить выполнение частичной агрегации на шардах. Рассмотрим запрос:

SELECT count(*) FROM t

Он имеет следующий физический план:

AggregateExec: mode=Final, gby=[], aggr=[count(*)]  CoalescePartitionsExec    RemoteExec: partitioning=UnknownPartitioning(3), filter=None      AggregateExec: mode=Partial, gby=[], aggr=[count(*)]        UnionExec          TarantoolExec: table=tcs.public.t fields=[], ...          BufExec: table=tcs.public.t fields=[], ...

Как мы видим, частичная агрегация была перенесена на шард. Заметим, что в этом примере для RemoteExec уже не выводится table, а также используется UnknownPartitioning вместо Hash. Это происходит из-за того, что после исполнения агрегации изменяется выходная схема: поле a в ней больше не присутствует, но появляется поле count(*).

То, какие узлы можно проталкивать внутрь для исполнения на одном шарде, определяется следующими факторами:

  1. Если в запросе указан фильтр, который читает лишь одно значение ключа шардирования (например, a = $1), то запрос может быть полностью локализован и план будет полностью протолкнут под RemoteExec.

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

Правило LocalizedPrepared

Правило оптимизации LocalizedPrepared позволяет полностью предотвратить передачу физического плана с экземпляра Scheduler на экземпляр Storage при исполнении обоих следующих условий:

  1. Исполняется аналитический запрос (prepared statement).
  2. План был полностью локализован.

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

PreparedStatementExec: name=query, mode=local {from_cache: true}  RemoteExec: table=tcs.public.sharded_by_indexed_string, partitioning=Hash([a@0], 1), filter=None    PreparedStatementExec: name=query, mode=remote, metrics=[]\n

Данный план следует читать так: "Исполняется аналитический запрос, локальный для экземпляра Scheduler. Его план состоит из однопартицированного RemoteExec, который отправляет удаленный план на экземпляр Storage для исполнения аналитического запроса."

В этом случае вместо тяжелого плана будет отослан всего один узел (PreparedStatementExec). Далее экземпляр Storage сам запланирует аналитический запрос с этим названием (либо возьмет план для исполнения из кэша) и исполнит его.

Модификация данных

Для выполнения операций модификации данных в Tarantool Column Store используются запросы SQL DML. Их можно отсылать как через SQL-драйвер, так и в виде POST-запроса на HTTP-адрес /sql.

Вставка данных

TCS поддерживает несколько вариантов вставки данных в таблицу:

  • SQL-запрос с помощью SQL-драйверов;
  • SQL-запрос на HTTP-адрес /sql;
  • HTTP-запрос с данными в формате JSON на HTTP-адрес /insert.

Вставка данных через SQL-драйверы

Для вставки данных в таблицу используются SQL-запросы с оператором INSERT вида INSERT INTO table_name { VALUES ( expression [, ...] ) [, ...] | query }.

В одном запросе можно передавать как один, так и несколько объектов для вставки. TCS обрабатывает объекты в том порядке, в каком они указаны в запросе.

В значениях полей можно указывать как конкретные значения, так и null.

Пример:

INSERT INTO target_table VALUES (1, 'One'), (2, 'Two');

Также в запросах можно использовать аргументы-заполнители (placeholders).

Вставка данных через /sql

Вставка данных чере HTTP-адрес /sql производится с помощью SQL-запросов, полностью аналогичных тем, что используются при вставке данных через SQL-драйверы. Алгоритм обработки запросов и их быстродействие здесь также аналогичны.

Вставка данных через /insert

HTTP-адрес /insert используется для выполнения запросов на вставку как более быстрая альтернатива SQL-драйверам и HTTP-адресу /sql.

Вставка через HTTP-адрес /insert работает быстрее, поскольку не требует ресурсоемкого построения планов. При этом способе вставки производятся лишь 2 дополнительных действия:

  • проверка соответствия типов переданных значений и типов колонок;
  • перевод данных в необходимый формат для вставки в Tarantool.

Через HTTP-адрес /insert данные передаются не с помощью SQL-запросов, а с помощью HTTP-запросов в сериализованном виде в формате JSON.

Требования к HTTP-запросам:

  • Запросы отсылаются на HTTP-адрес вида /insert/$table или /insert/$schema/$table, где /$schema – это имя схемы, а /$table – это имя таблицы. Если схема не указана, то по умолчанию применяется схема public.
  • Тело запроса должно содержать массив описаний вставляемых объектов.
  • Для каждой строки указываются значения вида "имя_колонки": значение. Их требуется указывать для всех колонок, которые есть в таблице.
  • Порядок колонок в запросе может быть любым. Например, [{ "a": 1, "b": 2 }] эквивалентно [{ "b": 2, "a": 1 }].
  • Значения null можно опускать. Например, [{ "a": 1, "b": null }] эквивалентно [{ "a": 1 }].
  • В заголовке запроса нужно обязательно указывать content-type: application/json.

Пример: вставка данных в таблицу target_table с помощью утилиты curl.

curl -u 'tcs:tcs' -H 'Content-Type: application/json' -d '[{"a":1, "b":-1}, {"a":2, "b": -2}]' http://localhost:7777/insert/target_table

Вставка данных из выборки (insert into select)

Для вставки в таблицу объектов из выборки используйте SQL-запрос вида INSERT INTO table_1 SELECT attribute FROM table_2.

Пример:

INSERT INTO names SELECT name FROM users

В операциях вставки из выборки можно использовать все имеющиеся в TCS возможности SELECT-запросов, например осуществлять выборку из нескольких таблиц (оператор JOIN) и задавать условия с помощью оператора WHERE. Подробнее см. Справочник по SQL > Инструкция SELECT.

Порядок полей таблиц и типы должны совпадать. Например, возьмем две таблицы:

  • таблицу names, которая содержит 2 поля, name и age (именно в таком порядке они объявлены в модели данных);
  • таблицу users, которая содержит поля address, age, name и прочие.

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

INSERT INTO names SELECT name, age FROM users

Обновление данных

Для обновление данных в таблице используйте запрос с SQL-оператором UPDATE.

Поддерживается следующий синтаксис запросов:

UPDATE table_nameSET column1 = value1, column2 = value2, ...WHERE condition;

Пример:

UPDATE namesSET name = 'Anna Danilova', age = '32'WHERE id = 10

Удаление данных

Для удаления объектов из таблиц используйте запрос с SQL-оператором DELETE.

Пример:

DELETE FROM a WHERE i > 1 LIMIT 100

Управление запросами

Просмотр списка активных запросов

Эта возможность не поддерживается в текущей версии TCS.

Отмена запроса

Эта возможность не поддерживается в текущей версии TCS.

Отправка нескольких SQL-инструкций (multi-statement)

На HTTP-адрес /sql можно отправлять сразу несколько SQL-инструкций, разделенных точкой с запятой (;). Это могут быть любые SQL-инструкции, поддерживаемые TCS.

TCS обрабатывает инструкции в указанном порядке и возвращает список статусов.

Если обработка какой-либо инструкции заканчивается ошибкой, то TCS прекращает обработку инструкций и возвращает список статусов.

Пример запроса:

POST http://localhost:7777/sql HTTP/1.1Content-Type: application/jsonSELECT name FROM users; INSERT INTO names SELECT name FROM users; SELECT * FROM names ORDER BY name

Если, например, вторая инструкция (INSERT INTO names SELECT name FROM users) завершится ошибкой, то третья инструкция тоже не выполнится.

Управление транзакциями

В текущей версии TCS ни в одном из интерфейсов (HTTP, JDBC\ADBC) не поддерживается интерактивное управление транзакциями (COMMIT, ROLLBACK).

Однако, если SQL-операция по изменению данных (DELETE, INSERT, UPDATE) затрагивает большой массив данных (больше максимального размера пакета), то она разбивается на несколько под-операций, которые далее обрабатываются как единая транзакция. При этом если какая-либо из под-операций завершается некорректно, то оставшиеся под-операции не выполняются, но (в отличие от классических транзакций) результат предыдущих под-операций не откатывается.

Пример: При удалении 50 тысяч строк создается 7 под-операций удаления (50000 / 8192 примерно равно 6,1). Если пятая под-операция из этих 7 завершится некорректно, то эта и остальные две под-операции не применятся, но результат первых 4 операций останется в базе.

Отправка параметризованных SQL-запросов по HTTP

TCS поддерживает отправку параметризованных SQL-запросов по HTTP. Параметры запроса передаются списком в соответствующей переменной:

  • args – список параметров при однократном вызове запроса (поддерживается для всех видов запросов):

    curl -u tcs:tcs -X POST -H "Content-Type: application/json" -d '{ "query": "SELECT * FROM t WHERE a>$1", "args": [0] }' http://localhost:7777/sql
  • bulk_args – список списков параметров для множественных вызовов (поддерживается для запросов INSERT, UPDATE, DELETE):

    curl -u tcs:tcs -X POST -H "Content-Type: application/json" -d '{ "query": "INSERT INTO t VALUES($1, $2)", "bulk_args": [[1,1], [2,2]] }' http://localhost:7777/sql

Принудительная маршрутизация запросов

В режиме проксирования экземпляры Scheduler могут принудительно отправлять запросы лишь на те экземпляры Storage, которые настроены на работу только в режиме чтения (read-only) – либо наборот, принудительно исключать такие экземпляры Storage из маршрутизации.

Для отправки запроса на определенный вид экземпляров Storage нужно в параметре заголовка x-tcs-route указать тот тип экземпляров Storage, на которые следует маршрутизировать запрос:

  • ro для отправки на экземпляры, настроенные только на чтение;
  • rw для исключения read-only экземпляров из маршрутизации (в этом случае запрос будет отправлен на экземпляр, настроенный на чтение-запись);
  • any для отправки на все типы экземпляров (это аналогично тому, когда заголовок x-tcs-route не указан).

Например, "x-tcs-route": "rw".

Балансировка нагрузки для таких запросов осуществляется только среди указанного типа экземпляров Storage.

Далее приводятся примеры отправки запросов на RW-экземпляр Storage для разных интерфейсов.

  • Для ADBC/JDBC:

    header_key = f"{DatabaseOptions.RPC_CALL_HEADER_PREFIX.value}x-tcs-route"             db_kwargs[header_key] = rw      self.conn = adbc_driver_flightsql.dbapi.connect(          self.uri,          db_kwargs=db_kwargs,          autocommit=True,      )
  • Для HTTP:

    curl --location-trusted -L -X POST -u tcs:tcs -H "x-tcs-route:rw" -d 'CREATE TABLE t(a int)' http://localhost:7780/sql

Включение схемы данных в ответ

По умолчанию, ответы на запросы к TCS содержат массив данных без указания схемы. Например:

$ curl -u tcs:tcs -d 'insert into t values(1)' http://127.0.0.1:7777/sql{"responses":[{"is_success":true,"rows":[{"count":1}],"timings":{"collect":"494.295µs"}}]}%

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

  • x-tcs-include-schema: true – в начале ответа возвращается схема данных в формате колонка:тип. Схема данных включается в ответ, даже если возвращается 0 строк. Например:

    $ curl -u tcs:tcs  -d 'insert into t values(1)' -H 'x-tcs-include-schema: true' http://127.0.0.1:7777/sql{"responses":[{"is_success":true,"schema":[{"name":"count","type":"UInt64"}],"rows":[{"count":1}],"timings":{"collect":"311.063µs"}}]}
  • x-tcs-response-format: ignite – в начале ответа возвращается схема данных в формате Apache Ignite. Сложные и простые данные выводятся по-разному.

    Пример вывода простых данных:

    {'schema': ['Int32', 'Utf8'], 'values': [[1, 'one'], [2, 'two'], [3, 'three']]}

    Пример вывода сложных данных:

    {'responses': [{'is_success': True, 'rows': [{'a': 1, 'b': 'one'}, {'a': 2, 'b': 'two'}, {'a': 3, 'b': 'three'}], 'timings': {'logical_plan': '1.852696ms', 'optimize_logical_plan': '791.525µs', 'execution_plan': '1.392713ms', 'pre_execution': '652.016µs', 'collect': '3.567623ms'}}]}

Работа со сложными запросами

TCS не накладывает ограничений на следующее:

  • глубина вложенности подзапросов;
  • количество таблиц\представлений, участвующих в запросе;
  • количество полей данных в результате;
  • количество подзапросов.

Разрешено использовать подзапросы, а также расчетные значения подзапросов в последующих подзапросах.

Работа с пользовательскими функциями и процедурами

Текущая версия TCS не поддерживает работу с пользовательскими функциями/процедурами.

Работа с индексами

TCS поддерживает работу с индексами двух типов:

TREE-индексы

По умолчанию, в TCS используются индексы типа TREE (дерево). Такой индекс устроен как самобалансирующееся дерево и работает по принципу key-value (ключ-значение). Благодаря такой структуре поиск, вставка и удаление происходят за время O(log n) (в отличие от классических индексов, построенных по принципу хеш-таблиц, где поиск происходит за время O(1)).

Особенности TREE-индексов в TCS

TREE-индексы можно строить по колонкам со следующими типами данных:

  • целочисленные (i8, i16, i32, i64),
  • беззнаковые целые (u8, u16, u32, u64),
  • строковый (utf8),
  • логический (bool),
  • временные метки (ts),
  • календарные даты (date32).

Не поддерживаются:

  • числа с плавающей точкой (f32, f64).

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

Этот вид индексов работает только для запросов на равенство значений. Запросы на сравнение значений (больше/меньше) пока не поддерживаются.

Создание TREE-индекса

См. CREATE INDEX.

Удаление TREE-индекса

См. DROP INDEX.

Перестроение TREE-индекса

Для всех TREE-индексов проводится автоматическая переиндексация по мере изменения данных в таблице. Принудительно перестраивать индексы вручную не требуется.

Первичные индексы

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

  • задан пользователем, например:

    CREATE TABLE t(    a int,    b int,    c int,    PRIMARY KEY(a))
  • либо создан автоматически, например:

    CREATE TABLE t(    a int,    b int,    c int)

    Здесь в таблицу t будет автоматически добавлено поле rowid (оно станет первым полем в таблице), а по нему будет создан первичный индекс.

У таблицы может быть только один первичный индекс. Все остальные индексы в таблице являются вторичными.

Первичный индекс должен быть уникальным.

Он может строиться как по одной колонке, так и по нескольким (см. составные индексы).

Вторичные индексы

Вторичные индексы могут быть как уникальными (UNIQUE), так и неуникальными.

Они могут строиться как по одной колонке, так и по нескольким (см. составные индексы).

Подробнее см. INSERT INDEX.

Составные индексы

Составной индекс может содержать более одной колонки.

Для создания составных TREE-индексов поддерживаются запросы следующего вида:

  • первичный составной индекс:

    CREATE TABLE t(    a int,    b int,    c int,    PRIMARY KEY(a, c))
  • вторичный составной индекс:

    CREATE INDEX SYNC test_index ON departments(name, manager, size);

Чтение с использованием индекса осуществляется при наличии условия (в т.ч. сортировки) по первому или по нескольким полям индекса. Например:

SELECT * FROM departments WHERE name = 'HR' AND manager != 'Ivanov' ORDER BY size;

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

Можно создавать как уникальные (UNIQUE), так и неуникальные составные индексы.

Подробнее см. INSERT INDEX.

Порядок сортировки по индексу

По умолчанию, все индексы в TCS создаются с возрастающим порядком сортировки. Например, инструкция CREATE INDEX t(a,b,c) имеет тот же эффект, что и CREATE INDEX t(a ASC, b ASC, c ASC).

При необходимости порядок сортировки для индекса, заданного пользователем (как первичного, так и вторичного), можно задать с помощью ключевых слов desc (убывающий порядок) и asc (возрастающий порядок). Для составных индексов порядок сортировки можно задавать индивидуально для каждой компоненты. Например:

-- первичный составной индексCREATE TABLE t(a int, b int, primary key(b DESC, a ASC))-- вторичный составной индексCREATE INDEX my_ind ON t(b DESC, a ASC)

Для первичного индекса, создаваемого автоматически (rowid), также можно явным образом задать порядок сортировки. Например:

CREATE TABLE t(a int, primary key(rowid DESC))

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

Порядок чтения по индексу

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

CREATE INDEX i ON t(a, rowid DESC)

В результате при чтении по индексу i данные будут предоставляться в порядке убывания rowid.

Также можно явно задавать порядок чтения по вторичному индексу, в состав которого входит автоматически создаваемый первичный индекс (rowid). Например:

CREATE INDEX sec ON t(a, rowid desc)

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

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

Для планировщика можно явно указать конкретный индекс (с заданным в этом индексе порядком чтения) с помощью функции plan_with с аргументом use_index.

Оптимизации при работе с TREE-индексами

Индексы (и первичные, и вторичные) оптимизируют запросы с указанием равенства и диапазона значений.

Для эффективной работы фильтрующее выражение в запросе должно сначала задавать условия равенства по первым компонентам индекса (если он составной), а затем – условия вхождения в диапазон для последующей компоненты: col1=x1, col2=x2, ..., coln in [ln, rn]

Например, если индекс состоит из одной компоненты (a), то с его использованием можно сделать запросы со следующими фильтрами:

a = 1a > 1a > 2 AND a < 3a < 3

Если индекс содержит две компоненты (a, b), то в запросе можно задать следующие фильтры:

a = 1 AND b > 2a = 1 AND b = 1a = 1 AND b > 3a > 1a > 1 AND a < 10

Индексы используются планировщиком:

  • при сканировании, например:

    EXPLAIN SELECT * FROM t WHERE a = 1 AND c = 2----logical_plan TableScan: t projection=[a, b, c], full_filters=[t.a = Int32(1), t.c = Int32(2)]physical_plan01)IndexScanExec: table=tcs.public.t, index=pk_tcs_public_t, direct_projection=[a, b, c]02)--Range: (a = 1) AND (c = 2)
  • при поиске по диапазону, например:

    EXPLAIN SELECT * FROM t WHERE a > $1 AND a < $10----logical_plan TableScan: t projection=[a, b, c], full_filters=[t.a > $1, t.a < $10]physical_plan01)IndexScanExec: table=tcs.public.t, index=pk_tcs_public_t, direct_projection=[a, b, c]02)--Range: $1 < a < $10

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

Таким образом, при сканировании исполняются проекции двух видов:

  • прямая проекция – проводится только с использованием первичного индекса.
  • косвенная проекция – проводится дополнительно для каждой записи с использованием вторичного индекса.

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

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

CREATE TABLE t(a int, b int, c int)-- создаем вторичный индексCREATE INDEX SYNC ta ON t(a)EXPLAIN SELECT * FROM t WHERE a = 1----logical_plan TableScan: t projection=[a, b, c], full_filters=[t.a = Int32(1)]physical_plan01)IndexScanExec: table=tcs.public.t, index=ta, direct_projection=[a, rowid], indirect_projection=[b, c]02)--Range: a = 1

Здесь планировщик производит выборку по индексу ta. Сначала он смог исполнить проекцию колонки a и rowid с помощью первичного индекса (прямая проекция). А для колонок b и c пришлось построчно исполнить косвенные проекции.

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

BRIN-индексы

Индексы типа BRIN (Block Range Index) предназначены для обработки больших таблиц, в которых значение в колонке имеет некоторую естественную корреляцию с физическим положением строки в таблице (например, временные метки создания записи). Однако в некоторых случаях BRIN-индексы дают выигрыш в быстродействии и на неотсортированных данных (см. Эффективность BRIN-индексов).

В настоящий момент TCS поддерживает только один тип BRIN-индексов – minmax.

BRIN-индексы строятся на основе метаданных, которые рассчитываются, хранятся и обновляются для каждого блока данных в индексируемой колонке. Для minmax-индексов этими метаданными является статистика по границам диапазонов значений колонки:

  • min - минимальное значение по колонке в блоке,
  • max - максимальное значение по колонке в блоке.

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

Например, в каком-то блоке минимальным значением колонки, по которой построен minmax-индекс, может быть 15. Если мы ищем строки, в которых значения колонки лежат в диапазоне от 5 до 10 (SELECT * FROM attributes WHERE column_a > 5 and column_a < 10), то такой блок нам не походит, и его можно пропустить целиком.

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

Эффективность BRIN-индексов

BRIN-индексы эффективны, когда данные в колонке имеют естественную сортировку (в таких случаях запрос без ORDER BY по колонке индекса возвращает её значения в порядке возрастания или убывания).

Для неотсортированных данных также можно применять BRIN-индексы. Разница в быстродействии по сравнению с TREE-индексами в этом случае зависит от количества записей. Если разные значения распределены по колонке равномерно, то нужно, чтобы выполнялось неравенство N/M >> K, где:

  • N – общее число записей в таблице;
  • M – размер одного блока (по умолчанию, 8192);
  • K – оценка числа записей, которые удовлетворяют условию по колонке с BRIN-индексом.

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

Примеры:

  • N = 50 000, M = 8192, K = 50. Здесь неравенство НЕ выполняется, блоки не отбрасываются. Запрос работает как full scan, но ещё и с дополнительной фильтрацией. Поэтому запрос выполняется медленнее, чем с full scan.
  • N = 6 500 000, M = 8192, K = 50. Здесь неравенство выполняется, некоторые блоки отбрасываются. Запрос работает не как full scan. Несмотря на дополнительную фильтрацию, запрос выполняется быстрее, чем с full scan.

Во всех случаях (отсортированные или неотсортированные данные) использование BRIN-индекса по колонке, по которой построен TREE-индекс, включающий только эту колонку, будет менее эффективным, чем поиск по этому TREE-индексу. В то же время, если другого индекса по колонке нет, а условие фильтрации использует только её, то поиск по BRIN-индексу будет эффективнее, чем поиск совсем без индекса (но в случае с неотсортированными данными – только при достаточном их объёме).

Особенности BRIN-индексов в TCS

Индексы minmax можно строить по колонкам со следующими типами данных:

  • целочисленные (i8, i16, i32, i64),
  • беззнаковые целые (u8, u16, u32, u64),
  • числа с плавающей точкой (f32, f64),
  • временные метки (ts),
  • календарные даты (date32).

Не поддерживаются:

  • строковый (utf8),
  • логический (bool).

Чтение с использованием minmax-индекса осуществляется автоматически для следующих случаев:

  • обработка запросов с условием типа WHERE col BETWEEN $left AND $right,
  • другого индекса по этой колонке нет, а условие фильтрации использует только её.

Создание BRIN-индекса

Чтобы построить minmax-индекс для колонки, нужно явно указать параметр USING minmax при вызове CREATE INDEX. Например:

CREATE INDEX users_brin ON users USING minmax (created_at);

В этом примере minmax-индекс построен по колонке (created_at).

Удаление BRIN-индекса

См. DROP INDEX.

Совместное использование разных типов индексов

В текущей версии TCS не поддерживается совместное использование разных типов индексов (tree, minmax) в одном запросе.

Вытеснение данных

Вытеснение данных по количеству записей

При добавлении новых записей сверх лимита одновременно хранимых записей наиболее старые записи удаляются.

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

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

  • при создании таблицы, например: CREATE TABLE t MAX_ROWS 10000
  • при изменении таблицы, например: ALTER TABLE t MAX_ROWS 100000

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

По умолчанию, лимит для таблиц не установлен: без явного указания лимита MAX_ROWS при создании таблицы вытеснение данных из нее не производится.

При необходимости можно в дальнейшем отключить вытеснение, задав значение лимита равное 0, например: ALTER TABLE t MAX_ROWS 0.

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

Вытеснение данных по времени

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

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

Настройка вытеснения для новых таблиц

Чтобы настроить вытеснение по времени для новой таблицы, нужно сделать вызов CREATE TABLE следующего вида:

CREATE TABLE table_name (    id u64,    created_at ts)[TTL expr    DELETE | INTO VOLUME 'volume_name'    SCHEDULE 'cron_expr']

где:

  • (Обязательно) Колонка типа ts (например, created_at ts). По этой колонке TCS создает вторичный индекс для быстрого поиска данных, подлежащих вытеснению.

  • (Обязательно) TTL expr – выражение, определяющее момент устаревания данных и перемещения их из основной таблицы (например, created_at + INTERVAL 7 DAYS). Выражение expr должно возвращать значение типа ts и имеет следующий формат:

    <timestamp_column> + INTERVAL <time_interval> <interval_type>

    где:

    • (Обязательно) <timestamp_column> – указанная выше колонка типа ts с временными метками;

    • (Oбязательно) INTERVAL – длительность хранения относительно временной метки, состоящая из целочисленного значения <time_interval> и типа интервала <interval_type> (например, DAYS, MONTHS, YEARS, HOURS).

  • (Обязательно) Действие по истечении TTL:

    • DELETE – удаление данных
    • INTO VOLUME 'volume' – перемещение данных на указанный внешний том
  • (Обязательно) SCHEDULE – частота проверки и вытеснения данных из основной таблицы. Задается с использованием синтаксиса планировщика crontab, например:

    • '@hourly' (каждый час)
    • '30 * * * *' (раз в 30 минут)

    Настройка ежедневной проверки с помощью сокращений '@daily' и @midnight не поддерживается. Вместо этого можно использовать выражение (0 0 * * *) (ежедневная проверка в полночь).

Пример 1: создание таблицы с вытеснением (удалением) данных старше 7 дней, с ежедневной проверкой.

CREATE TABLE my_table (    id u64,    created_at ts)TTL created_at + INTERVAL 7 DAYS    DELETE    SCHEDULE '(0 0 * * *)'

Пример 2: создание таблицы с вытеснением (охлаждением) данных старше 7 дней на внешний том, с ежедневной проверкой.

CREATE TABLE my_table (    id u64,    created_at ts)TTL created_at + INTERVAL 7 DAYS    INTO VOLUME 'my_volume'    SCHEDULE '(0 0 * * *)'

Настройка вытеснения для существующих таблиц

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

Также для таблиц с вытеснением данных разрешены следующие операции:

  • удаление/добавление колонок;
  • переименование таблицы.

Охлаждение данных

Особенности охлаждения данных в TCS

Возможности и ограничения текущей версии TCS:

  • Охлаждение данных работает во всех режимах работы кластера проксирования, шардирования).
  • Охлаждение настраивается только по TTL. Охлаждение по количеству записей (объему) и по прочим условиям (например, по триггеру) не поддерживается.
  • Настройка охлаждения доступна только для новых таблиц. Для уже существующих таблиц эта возможность не поддерживается.
  • Поддерживаются таблицы Apache Iceberg в формате Parquet.
  • Поддержаны все типы данных, кроме кроме u64 и decimal256.
  • Доступны метрики для мониторинга вытеснения и охлаждения данных.
  • События охлаждения записываются в журнал событий аудита.
  • Охлаждение осуществляется на локальные и примонтированные сетевые диски. Работа с хранилищами S3 не поддерживается. Также пока недоступно изменение существующего тома.
  • Охлаждение – однонаправленный процесс. После сброса на диск данные не возвращаются обратно в память. Это исключает сложные сценарии синхронизации данных и гарантирует, что логика охлаждения остается линейной и прозрачной. А в случае необходимости старые данные всегда доступны средствами SQL.
  • Данные на диске нельзя менять или удалять средствами SQL, в том числе и схему данных. Это соответствует природе исторических фактов: транзакция уже прошла, ее сумма не меняется. Для аналитики и отчетности режим «read-only» безопасен и исключает случайные изменения. Такой сценарий охлаждения подходит для исторических журналов, транзакционных лент и событий, которые не перезаписываются.
  • Вставка данных всегда производится сначала в оперативную память, где данные накапливаются и охлаждаются в соответствии с выбранной стратегией.

Управление томами

Создание томов для хранения данных

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

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

CREATE EXTERNAL VOLUME my_local_volume AS  '/mnt/tarantool_share/external_volume'WITH (  'format' = 'iceberg',  'db_file_name' = 'my-iceberg.catalog');

где:

  • (Обязательно) Путь к хранилищу указывается без префикса file://.

  • (Обязательно) Блок WITH содержит следующие параметры:

    • (Обязательно) format – формат описания таблиц. Поддерживается только одно значение: iceberg. Тем не менее этот параметр является обязательным.
    • (Необязательно) db_file_name – путь к тому в tcs_volumes_list() и DESCRIBE. В данном примере путь будет равен /tmp/AAA/my-iceberg.catalog. По умолчанию (если бы параметр db_file_name не был задан), путь бы равнялся /tmp/AAA/my_local_volume.

Получение информации о томах

Получение общей информации о томах

Для получения общей информации о подключенных внешних томах используется следующая команда:

SHOW EXTERNAL VOLUMES

Возвращаемые данные:

  • имя внешнего тома
  • формат данных
  • идентификатор URI (путь хранения данных)
  • количество таблиц, связанных с этим томом

Пример:

SELECT * from tcs_volumes_list() ORDER BY name----volume1  iceberg  sqlite:///tmp/A/volume1  42volume2  iceberg  sqlite:///tmp/B/volume2  10volume3  iceberg  sqlite:///tmp/C/volume3   0

Получение детальной информации о томе

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

DESCRIBE EXTERNAL VOLUME my_volume

где:

  • (Обязательно) my_volume – имя внешнего тома.

Помимо общей информации, в ответе возвращается:

  • имя набора реплик, который в случае формата описания таблиц Iceberg используется как пространство имен (namespace) для данных таблиц;
  • список связанных таблиц;
  • тип каталога.

Пример:

DESCRIBE EXTERNAL VOLUME volume1----volume1  iceberg  sqlite:///tmp/A/volume1 replicaset1  []  local-sqlite

Удаление тома

Для удаления внешнего тома используется команда следующего вида:

DROP EXTERNAL VOLUME [ IF EXISTS ] my_volume;

где:

  • (Необязательно) IF EXISTS – параметр, который предотвращает возникновение ошибки, если удаляемый том не существует.
  • (Обязательно) my_volume – имя удаляемого внешнего тома.

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

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

Управление таблицами с TTL (Time To Live)

См. Вытеснение данных по времени.

Чтение охлажденных данных

Для чтения определенного вида данных в рамках текущей сессии нужно установить переменную tcs.volume_read_mode:

  • SET tcs.volume_read_mode = 'skip' – не читать данные с диска и из временного буфера (limbo).

  • SET tcs.volume_read_mode = 'fallback' (по умолчанию) – сначала пытаться читать данные из памяти, если там пусто – читать с диска и из временного буфера (limbo).

  • SET tcs.volume_read_mode = 'union' – читать данные параллельно и из памяти, и с диска + limbo.

Например, для перехода в режим одновременного чтения "горячих" и "холодных" данных нужно перед запросом на чтение выставить значение переменной union:

SET tcs.volume_read_mode = 'union';SELECT * FROM my_table;

Управление вытеснением данных из limbo

Вытеснение данных из промежуточной таблицы (limbo) на диск производится автоматически на каждое изменение таблицы (ALTER TABLE), а также в случае, когда размер limbo достигает заданного лимита строк (параметр limbo_flush_threshold_rows). Для управления автоматической очисткой limbo используются следующие параметры конфигурации:

  • eviction.slice_ms – количество миллисекунд, которое TCS может потратить на удаление (flushing) вытесненных данных из limbo за одну итерацию.
  • eviction.limbo_flush_worker_threads – количество потоков, используемых задачами, которые выполняют удаление (flushing) вытесненных данных из limbo.
  • eviction.limbo_flush_threshold_rows – количество строк, по достижении которого производится очистка и запись содержимого limbo на диск.

Например, при лимите в 1 миллион строк (лимит по умолчанию) были вытеснены 500 тысяч строк. Сначала они останутся в limbo. Если в следующую итерацию будут вытеснены еще 500 тысяч строк (либо добавится колонка), то произойдет автоматическое вытеснение данных из limbo на диск (flush). Если же в следующую итерацию будет вытеснено меньше 500 тысяч строк, то автоматического вытеснения данных из limbo не случится.

Для форсированного ручного сброса данных из limbo на диск используется команда SELECT * FROM tcs_flush_limbo().

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

Во время перемещений данных между промежуточной таблицей (limbo) и диском обеспечивается консистентность данных: они не дублируются и не пропадают. Перед каждой операцией вытеснения данных из limbo TCS проводит синхронизацию снимков между снимком в Iceberg и мета-таблицей TCS. Если во время вытеснения происходит сбой, TCS выявляет рассинхронизацию. В случае, когда запись на диск прошла, но не удалось обновить мета-данные в TCS, система при следующей попытке вытеснения выполняет откат к последнему известному корректному снимку, что предотвращает дублирование строк.

Восстановление данных при сбоях записи происходит автоматически. Ручного вмешательства не требуется.

Сжатие данных

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

Для колонок можно настраивать следующие алгоритмы сжатия (один из или оба сразу):

Сжатие данных по словарю

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

Для настройки сжатия по словарю используется параметр ENCODING DICT, который нужно указать для соответствующих колонок при вызове CREATE TABLE. Например:

CREATE TABLE t (id int, status utf8 ENCODING DICT)

Сжатие данных с помощью LZ4

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

Для настройки сжатия с помощью LZ4 используется параметр COMPRESSION, который нужно указать для соответствующих колонок при вызове CREATE TABLE. Например:

CREATE TABLE t (id int, status utf8 COMPRESSION lz4)

При добавлении новой колонки можно аналогичным образом настроить для неё сжатие данных с помощью LZ4, указав параметр COMPRESSION при вызове ALTER TABLE. Например:

ALTER TABLE t ADD COLUMN category COMPRESSION lz4

В текущей версии TCS поддерживается только один формат сжатия: lz4.

При необходимости можно задать значение параметра acceleration (по умолчанию 1) , который позволяет изменять степень сжатия для повышения скорости обработки. Можно задавать любое целое число в диапазоне от 1 до 65537 (рекомендованные значения – от 10 до 1000). В результате изменяется тщательность анализа повторяющихся значений внутри блоков данных; сам размер блока при этом не меняется. Чем больше значение acceleration, тем быстрее работает сжатие и распаковка данных, но тем меньше оказывается коэффициент сжатия. Например:

CREATE TABLE t (id int, status utf8 COMPRESSION lz4 WITH (acceleration = 100))

Совместное использование алгоритмов сжатия данных

TCS позволяет настраивать оба алгоритма сжатия для одной и той же колонки. Например:

CREATE TABLE t (id int, status utf8 COMPRESSION lz4 ENCODING DICT)

Работа с драйверами JDBC/ADBC

TCS поддерживает работу с общедоступными драйверами для интерфейсов JDBC и ADBC по SQL-протоколу Apache Arrow Flight.

Установка драйверов

Скачайте установочный пакет нужного драйвера по ссылке:

Подключение драйверов

Подключение драйвера JDBC к редактору DBeaver

  1. В DBeaver добавьте драйвер JDBC для Arrow Flight SQL в качестве нового драйвера:

    a. В меню выберите пункт Database > Driver Manager.

    b. В диалоговом окне Driver manager нажмите Create.

    c. В разделе Settings выполните следующие действия:

    • В поле Name укажите имя драйвера, например JDBC Driver for Arrow Flight SQL.
    • В поле Driver Type убедитесь, что выбран тип драйвера Generic.
    • В поле Class Name укажите org.apache.arrow.driver.jdbc.ArrowFlightJdbcDriver.
    • В поле URL Template укажите jdbc:arrow-flight-sql://{host}:{port} (например, jdbc:arrow-flight-sql://localhost:50051).
    • В поле Default Port укажите порт 50051.

    d. В разделе Libraries нажмите Add File и выберите .jar-файл для драйвера JDBC для Arrow Flight SQL.

    e. Нажмите OK.

  2. Создайте подключение к DBeaver, использующее ваш драйвер:

    a. Выберите Database > New Connection from JDBC URL.

    b. В поле Create New Connection в диалоговом окне JDBC URL введите jdbc:arrow-flight-sql://{host}:{port}?useEncryption=0 (например, jdbc:arrow-flight-sql://localhost:50051?useEncryption=0).

    Свойство useEncryption=0 (false) необходимо указывать явно, поскольку при работе с текущей версией TCS поддерживаются только незашифрованные JDBC-соединения. Значение по умолчанию для этого свойства равно 1 (true).

    В результате DBeaver добавит ваш драйвер в список Drivers.

    c. Выберите ваш драйвер в списке Drivers и нажмите Next.

    d. В диалоговом окне Connect to a Database укажите свои учетные данные для аутентификации. При работе с текущей версией TCS поддерживаются только JDBC-соединения по имени пользователя и паролю.

    Учетные данные задаются в конфигурации TCS.

    e. (необязательно) Нажмите Test Connection. Если подключение работает, откроется диалоговое окно Connection Test, в котором указывается, что DBeaver может подключиться к TCS. После попытки подключения соединение закрывается. Нажмите OK.

    f. Нажмите Finish.

Подключение драйвера ADBC к Apache Superset

  1. Склонируйте локально репозиторий Apache Superset https://github.com/apache/superset.

  2. В локальном репозитории Apache Superset создайте файл docker/requirements-local.txt и впишите в него пакеты с зависимостями:

    pyarrow==15.0.0flightsql-dbapi==0.2.2adbc-driver-flightsql==1.4.0psycopg2-binary==2.9.10
  3. Соберите и запустите Docker-образ Apache Superset с помощью Docker Compose:

    export TAG=4.1.1docker compose -f docker-compose-image-tag.yml up

    Подробнее см. https://superset.apache.org/docs/installation/docker-compose/.

  4. Подключитесь к веб-интерфейсу Apache Superset по HTTP-адресу http:/localhost:8088.

  5. Добавьте новое соединение datafusion+flightsql://tcs:tcs@{host}:{port}?insecure=True (например, datafusion+flightsql://tcs:tcs@host.docker.internal:50051?insecure=True).

    Подробнее см. https://superset.apache.org/docs/configuration/databases#connecting-through-the-ui.

Параметры строки соединения

Параметры строки соединения для каждого драйвера описаны в документации Apache Arrow:

Работа с индексами

Работа с индексами через JDBC

Для работы с первичными индексами через JDBC доступна функция getPrimaryKeys(), которая возвращает описания колонок всех первичных индексов для указанной таблицы.

См. подробнее в документации Oracle.

Работа с индексами через ADBC

Драйвер ADBC, использующийся в текущей версии TCS, не поддерживает работу с индексами.

Работа с нематериализованными SQL-представлениями

  • Создание SQL-представления: доступно через SQL-запрос.

  • Просмотр списка всех доступных SQL-представлений:

    • В редакторе DBeaver: нажать кнопку Refresh, все доступные SQL-представления отобразятся в списке Views.
    • В редакторе Superset: нажать кнопку Refresh, все доступные SQL-представления отобразятся в списке TABLE.
  • Просмотр текста SQL-представления: в любом редакторе выбрать SQL-представление в списке, рядом отобразится его текст.

  • Замещение SQL-представления: доступно через SQL-запрос.

  • Удаление SQL-представления: доступно через SQL-запрос.

Работа с аналитическими расчетами через JDBC/ADBC

Подробнее про аналитическиа расчеты (prepared statements) в TCS см. в разделе Аналитические расчеты.

Работа с аналитическими расчетами через JDBC

Требуется JDBC-драйвер flight-sql-dbc-driver версии не ниже 18.3.0.

Для работы с этим драйвером нужно установить зависимость в клиентском приложении.

Пример: добавление зависимости в pom.xml:

<dependency>    <groupId>org.apache.arrow</groupId>    <artifactId>flight-sql-jdbc-driver</artifactId>    <version>18.3.0</version></dependency>

Пример: строка соединения:

jdbc:arrow-flight-sql://localhost:50051?useEncryption=false&user=tcs&password=tcs

Работа с аналитическими расчетами через ADBC

Требуется драйвер go adbc версии не ниже 1.7.0.

Пример кода создания/вызова:

prepared, err := db.Prepare("SELECT * FROM t WHERE a=$1")if err != nil {    log.Fatalf("Failed to prepare the statement: %v", err)}var num int32var str stringif err = prepared.QueryRow(1).Scan(&num, &str); err != nil {    log.Fatalf("Failed to execute query: %v", err)}fmt.Println(num, str)

Пакетная загрузка данных через JDBC

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

INSERT INTO таблица (колонка1, колонка2) VALUES ($1, $2);

Пример кода:

static String singleRowPreparedInsert = "INSERT INTO t VALUES($1, $2, $3, $4)";static void runBatchInsert(Connection conn) throws SQLException {    try (var statement = conn.prepareStatement(singleRowPreparedInsert)) {        for (int i = 2; i <= 5; ++i) {            statement.setInt(1, i);            statement.setInt(2, i);            statement.setString(3, String.format("%d", i));            statement.setBoolean(4, true);            statement.addBatch();        }        statement.executeBatch();    }}

Работа с CIAM-аутентификацией через JDBC/ADBC

Для ADBC-клиентов, для работы с CIAM-аутентификацией требуется выполнить (см. настройки для работы с сессиями).

Для JDBC-клиентов:

  • Для работы с аутентификацией в режиме ciam нужно указать JWT-токен в качестве источника информации в параметре token. См. документацию.
  • Работа с аутентификацией в режиме simple в текущей версии TCS не поддерживается.

Работа с сессиями через JDBC/ADBC

Порядок работы с сессиями в TCS разнится в зависимости от протокола:

  • Для протокола HTTP каждый запрос выполняется в отдельной сессии. Она автоматически открывается при получении запроса и закрывается по окончании обработки.

  • Для протокола Arrow Flight SQL сессия также создается автоматически. Нет необходимости открывать ее явным образом с помощью запроса Handshake до отсылки SQL-запросов.

    Закрытие сессии происходит по запросу CloseSession. Также сессия закрывается автоматически по истечении макcимальной длительности сессии (параметр session_expiration_secs, по умолчанию – 8 часов).

    Параметры сессии можно устанавливать с помощью инструкции SET. Запрос SetSessionOptions при работе с сессиями не поддерживается.

    Подробнее см. документацию Arrow Flight RPC и Arrow Flight SQL.

Сессия, создаваемая на экземпляре Scheduler, используется для инициализации сессии с тем же номером (session_id) и аутентификационной информацией на экземпляре Storage при вызове методов Arrow Flight SQL API на стороне Storage со стороны Scheduler. При закрытии родительской сессии на экземпляре Scheduler автоматически закрываются все дочерние сессии, открытые с тем же session_id на экземплярах Storage.

Для работы с сессиями, клиенты должны поддерживать HTTP/2 cookie.

Работа с сессиями через JDBC

JDBC-драйвер flight-sql-jdbc-driver не требует дополнительной настройки для работы с сессиями и HTTP/2 cookie.

Работа с сессиями через ADBC

ADBC-клиенты требуют настроить поддержку HTTP/2 cookie:

  • Для протокола HTTP дополнительная настройка на стороне клиента не нужна.

  • Для протокола Arrow Flight SQL требуется следующая настройка:

    • для клиентских библиотек на Golang, в опциях подключения к Arrow Flight SQL надо включить cookie middleware:
import (    "database/sql"    // Import the FlightSQL ADBC driver for database/sql.    _ "github.com/apache/arrow-adbc/go/adbc/sqldriver/flightsql")...dsn := fmt.Sprintf("uri=%s; adbc.flight.sql.rpc.with_cookie_middleware=true;username=%s;password=%s", *uri, *username, *password)db, err := sql.Open("flightsql", dsn)
  • для клиентской библиотеки adbc_driver_flightsql на Python, нужно включить cookie middleware для Golang (поскольку данная библиотека построена на ADBC-клиенте Golang), а также установить опицю adbc.flight.sql.authorization_header:
from adbc_driver_flightsql import DatabaseOptionsimport adbc_driver_flightsql.dbapidb_kwargs = {    # enable cookies for session management    DatabaseOptions.WITH_COOKIE_MIDDLEWARE.value: "true",}if args.username is not None and args.password is not None:    db_kwargs["username"] = args.username    db_kwargs["password"] = args.passwordif args.token is not None:    db_kwargs["adbc.flight.sql.authorization_header"] = f"Bearer {args.token}"conn = adbc_driver_flightsql.dbapi.connect(f"grpc://{args.uri}", db_kwargs=db_kwargs)
  • При использовании балансирования нагрузки, для клиентов Arrow Flight SQL нужно настроить sticky sessions.

Масштабирование

Репликация

Поддерживаются стандартные функции синхронной/асинхронной репликации Tarantool.

Шардирование

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

Для запросов на чтение в режиме шардирования предусмотрены специальные оптимизации.

Особенности работы шардирования в TCS

Ограничения текущей версии TCS:

  • При переходе на шардированную конфигурацию требуется полная перезаливка данных.

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

  • Распределение данных между шардами осуществляется на основе актуальной конфигурации кластера. Любые изменения в конфигурации наборов реплик после начальной загрузки данных могут вызвать нарушения в работе механизма шардирования. К критическим изменениям относятся:

    • Переименование существующих наборов реплик;
    • Удаление наборов реплик;
    • Добавление новых наборов реплик.

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

Создание шардированных таблиц

Для создания шардированной таблицы используется команда CREATE TABLE с параметром SHARDED BY, где указывается ключ, по которому будет проводиться шардирование.

Ключ шардирования может быть строкой, числом или составным ключом.

При выборе ключа рекомендуется руководствоваться несколькими принципами:

  • запросы должны быть максимально локализованы;
  • данные должны быть сбалансированы по ключу.

Пример: создание шардированной таблицы с составным ключом

CREATE TABLE t(    a i32,    b utf8,    c u64,    d bool)SHARDED BY a, b

Шардированные таблицы можно создавать, только если кластер работает в режиме шардирования.

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