Руководство пользователя
Аутентификация пользователей в TCS производится с помощью компонента CIAM.
Для обращения к данным в хранилище Tarantool Column Store используются SQL-запросы.
TCS поддерживает следующие способы обращения к данным:
- SQL-запросы
SELECTчерез SQL и HTTP API – наиболее простой способ, подходящий для работы без нагрузки, например, для тестирования и отладки или единичного выполнения. - Аналитические расчеты – хранимые в TCS именованные процедуры, в которых доступен широкий спектр операций над данными с использованием всех поддерживаемых функций и операторов SQL.
- Представления для чтения (read views) – снимки хранилища данных в конкретный момент времени.
- Нематериализованные SQL-представления (non-materialized views in SQL) - удобный способ обращаться к большим запросам, как к таблице.
Поддерживаемые операторы и функции SQL описаны в Справочнике по SQL.
При работе с шардированным кластером полезно учитывать правила оптимизации запросов на чтение шардированных данных.
При настроенном охлаждении данных TCS позволяет выбирать режим чтения для "горячих" и "холодных" данных.
Для выполнения запроса 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
TCS предполагает работу под постоянной транзакционной нагрузкой на запись. Для консистентности чтения данных в таких условиях TCS использует представления для чтения (read view) – снимки хранилища данных в конкретный момент времени.
В каждый момент времени в системе существует одно актуальное представление на чтение.
Оно обновляется раз в rv_update_ms миллисекунд.
Также есть очередь из используемых в данный момент представлений, из которых еще выполняется чтение в проходящих обработку запросах SQL и прочих.
Когда запрос на чтение обработан, и при этом уже есть более актуальное представление, то представление, которые использовалось для этого запроса, помещается в очередь. При следующей итерации цикла сборщика мусора все представления из очереди удаляются.
Для того, чтобы гарантировать актуальность возвращаемых данных в период между обновлениями представлений для чтения, используется механизм буфера дельт. Он позволяет учесть все изменения, которые успели произойти с момента создания текущего представления.
См. подробнее:
- Чтение данных и буфер дельт;
- Статья о представлениях для чтения в документации Tarantool EE.
Нематериализованные SQL-представления удобны тем, что позволяют обращаться к большим запросам, как к таблице. Поскольку создаваемое представление не имеет физической формы, указанный запрос будет выполняться каждый раз при обращении к этому представлению.
Чтобы создать нематериализованное SQL-представление, отправьте SQL-запрос, содержащий SQL-оператор CREATE VIEW.
Пример: SQL-представление, содержащее список имен всех совершеннолетних лиц в таблице.
CREATE VIEW adults AS SELECT name FROM users WHERE age >= 18;
После создания нематериализованное SQL-представление становится доступно:
- для запросов на чтение,
- для аналитических расчетов,
- для использования в других нематериализованных SQL-представлениях; это означает, что в TCS можно создавать вложенные (иерархические) нематериализованные SQL-представления.
Пример: обращение к SQL-представлению adults из SELECT-запроса.
SELECT salary FROM users WHERE name IS IN (SELECT name FROM adults)
Чтобы просмотреть все доступные нематериализованные 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 предусмотрены правила оптимизации для запросов на чтение шардированных данных:
RemotePushDownLocalizedPrepared
Правило оптимизации RemotePushDown позволяет исполнить всю возможную часть запроса
на одном шарде, для того чтобы по сети пересылался минимум данных. Без этой оптимизации
все данные из таблицы, необходимые для выполнения запроса, пересылались бы по сети
на экземпляр Scheduler, где дальше исполнялся бы сам запрос.
Далее рассмотрим на примерах, как это правило оптимизации влияет на обработку шардированных
запросов на чтение. Предположим, что у нас есть шардированная таблица t(a, b) и 3 шарда.
Рассмотрим простой запрос:
SELECT * FROM t
Запросив EXPLAIN на экземпляре Scheduler, можно увидеть физический план следующего вида:
RemoteExec: table=tcs.public.t, partitioning=Hash([a@0], 3), filter=NoneUnionExecTarantoolExec: 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=1CoalesceBatchesExec: target_batch_size=8192FilterExec: a@0 = 1UnionExecTarantoolExec: 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 = 1RemoteExec: table=tcs.public.t, partitioning=Hash([a@0], 1), filter=tcs.public.t.a=1UnionExecTarantoolExec: 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(*)]CoalescePartitionsExecRemoteExec: partitioning=UnknownPartitioning(3), filter=NoneAggregateExec: mode=Partial, gby=[], aggr=[count(*)]UnionExecTarantoolExec: table=tcs.public.t fields=[], ...BufExec: table=tcs.public.t fields=[], ...
Как мы видим, частичная агрегация была перенесена на шард. Заметим, что в этом примере
для RemoteExec уже не выводится table, а также используется UnknownPartitioning
вместо Hash. Это происходит из-за того, что после исполнения агрегации изменяется
выходная схема: поле a в ней больше не присутствует, но появляется поле count(*).
То, какие узлы можно проталкивать внутрь для исполнения на одном шарде, определяется следующими факторами:
-
Если в запросе указан фильтр, который читает лишь одно значение ключа шардирования (например,
a = $1), то запрос может быть полностью локализован и план будет полностью протолкнут подRemoteExec. -
В ином случае проталкивание производится до тех пор, пока шард может исполнить соответствующий узел самостоятельно (то есть пока для сбора данных шарду не нужны данные с других шардов). Например,
CoalescePartitionsExecне может быть исполнен локально, поскольку для него необходимо собрать данные со всех шардов, тогда какFilterExecможет быть исполнен локально.
Правило оптимизации LocalizedPrepared позволяет полностью предотвратить передачу
физического плана с экземпляра Scheduler на экземпляр Storage при исполнении
обоих следующих условий:
- Исполняется аналитический запрос (prepared statement).
- План был полностью локализован.
В таком случае при исполнении аналитического запроса будет использоваться план следующего вида:
PreparedStatementExec: name=query, mode=local {from_cache: true}RemoteExec: table=tcs.public.sharded_by_indexed_string, partitioning=Hash([a@0], 1), filter=NonePreparedStatementExec: 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-запросы с оператором INSERT
вида INSERT INTO table_name { VALUES ( expression [, ...] ) [, ...] | query }.
В одном запросе можно передавать как один, так и несколько объектов для вставки. TCS обрабатывает объекты в том порядке, в каком они указаны в запросе.
В значениях полей можно указывать как конкретные значения, так и null.
Пример:
INSERT INTO target_table VALUES (1, 'One'), (2, 'Two');
Также в запросах можно использовать аргументы-заполнители (placeholders).
Вставка данных чере HTTP-адрес /sql производится с помощью SQL-запросов, полностью аналогичных
тем, что используются при вставке данных через SQL-драйверы.
Алгоритм обработки запросов и их быстродействие здесь также аналогичны.
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
Для вставки в таблицу объектов из выборки используйте 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.
На 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 операций останется в базе.
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] = rwself.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 поддерживает работу с индексами двух типов:
По умолчанию, в TCS используются индексы типа TREE (дерево).
Такой индекс устроен как самобалансирующееся дерево и работает по принципу key-value (ключ-значение).
Благодаря такой структуре поиск, вставка и удаление происходят за время O(log n)
(в отличие от классических индексов, построенных по принципу хеш-таблиц, где поиск происходит за время O(1)).
TREE-индексы можно строить по колонкам со следующими типами данных:
- целочисленные (
i8,i16,i32,i64), - беззнаковые целые (
u8,u16,u32,u64), - строковый (utf8),
- логический (bool),
- временные метки (
ts), - календарные даты (
date32).
Не поддерживаются:
- числа с плавающей точкой (
f32,f64).
На пустой таблице TREE-индексы создаются пустыми и наполняются по мере вставки новых данных. Но если на момент создания индекса в таблице уже есть данные, то они будут автоматически проиндексированы.
Этот вид индексов работает только для запросов на равенство значений. Запросы на сравнение значений (больше/меньше) пока не поддерживаются.
См. CREATE INDEX.
См. DROP INDEX.
Для всех 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.
Индексы (и первичные, и вторичные) оптимизируют запросы с указанием равенства и диапазона значений.
Для эффективной работы фильтрующее выражение в запросе должно сначала задавать
условия равенства по первым компонентам индекса (если он составной), а затем – условия
вхождения в диапазон для последующей компоненты: 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 (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-индексы эффективны, когда данные в колонке имеют естественную сортировку (в таких случаях запрос без 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-индексу будет эффективнее, чем поиск совсем без индекса (но в случае с неотсортированными данными – только при достаточном их объёме).
Индексы minmax можно строить по колонкам со следующими типами данных:
- целочисленные (
i8,i16,i32,i64), - беззнаковые целые (
u8,u16,u32,u64), - числа с плавающей точкой (
f32,f64), - временные метки (
ts), - календарные даты (
date32).
Не поддерживаются:
- строковый (utf8),
- логический (bool).
Чтение с использованием minmax-индекса осуществляется автоматически для следующих случаев:
- обработка запросов с условием типа
WHERE col BETWEEN $left AND $right, - другого индекса по этой колонке нет, а условие фильтрации использует только её.
Чтобы построить minmax-индекс для колонки, нужно явно указать параметр USING minmax
при вызове CREATE INDEX. Например:
CREATE INDEX users_brin ON users USING minmax (created_at);
В этом примере minmax-индекс построен по колонке (created_at).
См. 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 exprDELETE | 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 DAYSDELETESCHEDULE '(0 0 * * *)'
Пример 2: создание таблицы с вытеснением (охлаждением) данных старше 7 дней на внешний том, с ежедневной проверкой.
CREATE TABLE my_table (id u64,created_at ts)TTL created_at + INTERVAL 7 DAYSINTO VOLUME 'my_volume'SCHEDULE '(0 0 * * *)'
В текущей версии TCS нельзя настроить вытеснение данных для существующей таблицы, если оно не было настроено изначально при создании таблицы. Однако есть возможность поменять заданные ранее настройки вытеснения с помощью ALTER TABLE.
Также для таблиц с вытеснением данных разрешены следующие операции:
- удаление/добавление колонок;
- переименование таблицы.
Возможности и ограничения текущей версии 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 блокирует операцию удаления, формирует сообщение об ошибке и предоставляет полный список таблиц, зависящих от данного тома.
См. Вытеснение данных по времени.
Для чтения определенного вида данных в рамках текущей сессии нужно установить переменную
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) на диск производится автоматически на каждое
изменение таблицы (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 используется параметр 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)
TCS поддерживает работу с общедоступными драйверами для интерфейсов JDBC и ADBC по SQL-протоколу Apache Arrow Flight.
Скачайте установочный пакет нужного драйвера по ссылке:
- JDBC: https://repo1.maven.org/maven2/org/apache/arrow/flight-sql-jdbc-driver
- ADBC: https://pypi.org/project/adbc-driver-flightsql/
-
В 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.
- В поле Name укажите имя драйвера, например
-
Создайте подключение к 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.
-
Склонируйте локально репозиторий Apache Superset https://github.com/apache/superset.
-
В локальном репозитории Apache Superset создайте файл
docker/requirements-local.txtи впишите в него пакеты с зависимостями:pyarrow==15.0.0flightsql-dbapi==0.2.2adbc-driver-flightsql==1.4.0psycopg2-binary==2.9.10 -
Соберите и запустите 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/.
-
Подключитесь к веб-интерфейсу Apache Superset по HTTP-адресу
http:/localhost:8088. -
Добавьте новое соединение
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: https://arrow.apache.org/docs/java/flight_sql_jdbc_driver.html
- ADBC: https://arrow.apache.org/adbc/current/python/api/adbc_driver_flightsql.html
Для работы с первичными индексами через JDBC доступна функция getPrimaryKeys(),
которая возвращает описания колонок всех первичных индексов для указанной таблицы.
См. подробнее в документации Oracle.
Драйвер ADBC, использующийся в текущей версии TCS, не поддерживает работу с индексами.
-
Создание SQL-представления: доступно через SQL-запрос.
-
Просмотр списка всех доступных SQL-представлений:
- В редакторе DBeaver: нажать кнопку
Refresh, все доступные SQL-представления отобразятся в спискеViews. - В редакторе Superset: нажать кнопку
Refresh, все доступные SQL-представления отобразятся в спискеTABLE.
- В редакторе DBeaver: нажать кнопку
-
Просмотр текста SQL-представления: в любом редакторе выбрать SQL-представление в списке, рядом отобразится его текст.
-
Замещение SQL-представления: доступно через SQL-запрос.
-
Удаление SQL-представления: доступно через SQL-запрос.
Подробнее про аналитическиа расчеты (prepared statements) в TCS см. в разделе Аналитические расчеты.
Требуется 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
Требуется драйвер 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 осуществляется с помощью вставки следующего вида:
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();}}
Для ADBC-клиентов, для работы с CIAM-аутентификацией требуется выполнить (см. настройки для работы с сессиями).
Для JDBC-клиентов:
- Для работы с аутентификацией в режиме
ciamнужно указать JWT-токен в качестве источника информации в параметреtoken. См. документацию. - Работа с аутентификацией в режиме
simpleв текущей версии TCS не поддерживается.
Порядок работы с сессиями в 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-драйвер flight-sql-jdbc-driver не требует дополнительной настройки для работы с сессиями и HTTP/2 cookie.
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 managementDatabaseOptions.WITH_COOKIE_MIDDLEWARE.value: "true",}if args.username is not None and args.password is not None:db_kwargs["username"] = args.usernamedb_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:
-
При переходе на шардированную конфигурацию требуется полная перезаливка данных.
-
Режим шардирования (параметр
mode.sharded) не рекомендуется включать, если в конфигурации кластера не заданы хотя бы 2 шарда. -
Распределение данных между шардами осуществляется на основе актуальной конфигурации кластера. Любые изменения в конфигурации наборов реплик после начальной загрузки данных могут вызвать нарушения в работе механизма шардирования. К критическим изменениям относятся:
- Переименование существующих наборов реплик;
- Удаление наборов реплик;
- Добавление новых наборов реплик.
В случае проведения подобных операций после изменения конфигурации необходима полная перезаливка данных для обеспечения корректной работы системы шардирования.
Для создания шардированной таблицы используется команда CREATE TABLE с параметром SHARDED BY, где указывается ключ, по которому будет проводиться шардирование.
Ключ шардирования может быть строкой, числом или составным ключом.
При выборе ключа рекомендуется руководствоваться несколькими принципами:
- запросы должны быть максимально локализованы;
- данные должны быть сбалансированы по ключу.
Пример: создание шардированной таблицы с составным ключом
CREATE TABLE t(a i32,b utf8,c u64,d bool)SHARDED BY a, b
Шардированные таблицы можно создавать, только если кластер работает в режиме шардирования.
Как и все прочие запросы к кластеру в режиме шардирования, запросы на создание таблицы следует отправлять через экземпляр Scheduler, а не напрямую через экземпляр Storage.