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

Version:

0.x

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

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

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

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

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

Для выполнения запроса SELECT отправьте POST-запрос с его текстом на HTTP-адрес /sql.

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

POST http://localhost:7777/sql HTTP/1.1
Content-Type: application/json

SELECT * FROM db.public.user

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

POST http://localhost:7777/sql HTTP/1.1
Content-Type: application/json
  
SELECT * FROM db.public.users WHERE id=1

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

POST http://localhost:7777/sql HTTP/1.1
Content-Type: application/json
  
SELECT * FROM users WHERE id=1

Потоковые SQL-запросы

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

Для выполнения потокового запроса отправьте POST-запрос с его текстом на HTTP-адрес /streaming/sql.

Примечание

Обратите внимание, что для обработки запросов на HTTP-адресе /streaming/sql также используется отдельный сервер, чей адрес указывется в конфигурационном параметре http_listen_streaming (по умолчанию 0.0.0.0:7877).

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

POST http://localhost:7877/streaming/sql HTTP/1.1
Content-Type: application/json

SELECT * FROM db.public.user

Примечание

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

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

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

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

Примечание

Ограничения:

  • Для управления аналитическими расчетами используется только HTTP-адрес /computation.

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

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

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

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

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

POST http://localhost:7777/computation HTTP/1.1

{
  "sql": "PREPARE plan(INT) AS SELECT count(*) FROM db.public.users WHERE age > $1"
}

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

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

Чтобы выполнить существующий в TCS расчет, отправьте POST-запрос на HTTP-адрес /computation/run. JSON-тело запроса должно содержать массив пар имя расчета:аргументы.

Пример:

POST http://localhost:7777/computation/run HTTP/1.1

[
	{
    "name": "compute1",
    "args": [ 1 ]
  },
	{
    "name": "compute2",
    "args": [ 2 ]
   }
]

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

Подробнее см. Просмотр фактического времени выполнения запроса и Ограничение времени выполнения запроса.

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

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

Чтобы просмотреть все доступные расчеты, отправьте запрос GET на HTTP-адрес /computation:

GET http://localhost:7777/computation HTTP/1.1

Чтобы просмотреть текст конкретного расчета, отправьте запрос GET на HTTP-адрес /computation/<NAME>:

GET http://localhost:7777/computation/plan HTTP/1.1

Чтобы удалить запрос, отправьте запрос DELETE на HTTP-адрес /computation/<NAME>:

DELETE http://localhost:7777/computation/plan HTTP/1.1

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

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

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

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

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

Подробнее о представлениях на чтение читайте в документации Tarantool EE.

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

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

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

Чтобы создать нематериализованное SQL-представление, воспользуйтесь любым из двух способов:

  • Отправьте запрос POST на HTTP-адрес /ddl/sql. Тело запроса должно содержать SQL-оператор CREATE OR REPLACE VIEW.

  • Отправьте запрос POST на HTTP-адрес вида ddl/view/<NAME>/create, например /ddl/view/adults/create. Тело запроса должно содержать JSON-объект с одним полем sql. В качестве его значения используйте SQL-оператор SELECT (не CREATE OR REPLACE VIEW).

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

  • на HTTP-адрес /ddl/sql:

    POST http://localhost:7777/ddl/sql
    
    CREATE OR REPLACE VIEW adults AS SELECT name FROM db.public.users WHERE age >= 18;
    
  • на HTTP-адрес вида /ddl/view/<NAME>/create:

    POST http://localhost:7777/ddl/view/adults/create
    {
      "sql": "SELECT name FROM db.public.users WHERE age >= 18;"
    }
    

Примечание

Созданные SQL-представления существуют только на мастер-узле хранилища Tarantool и не сохраняются между перезагрузками.

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

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

  • для запросов через HTTP-адрес /sql,

  • для аналитических расчетов через HTTP-адрес /computation,

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

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

SELECT salary FROM db.public.users WHERE name IS IN (SELECT name FROM adults)

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

Чтобы просмотреть все доступные нематериализованные SQL-представления, отправьте запрос GET на HTTP-адрес /ddl/view:

GET http://localhost:7777/ddl/view HTTP/1.1

Чтобы просмотреть текст конкретного SQL-представления, отправьте запрос GET на HTTP-адрес вида ddl/view/<NAME>, например:

GET http://localhost:7777/ddl/view/adults HTTP/1.1

Чтобы заместить существующее SQL-представление, отправьте запрос POST с инструкцией вида CREATE OR REPLACE VIEW name[(column_name [, ...])] AS query на HTTP-адрес ddl/sql, например:

POST http://localhost:7777/ddl/sql HTTP/1.1

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

Чтобы удалить SQL-представление, воспользуйтесь любым из двух способов:

  • Отправьте запрос POST на HTTP-адрес /ddl/sql. Тело запроса должно содержать SQL-оператор DELETE VIEW с указанием имени представления, например:

    POST http://localhost:7777/ddl/sql HTTP/1.1
    
    DROP VIEW adults
    
  • Отправьте пустой запрос POST на HTTP-адрес вида ddl/view/<NAME>/delete, например /ddl/view/adults/delete:

    POST http://localhost:7777/ddl/view/adults/delete HTTP/1.1
    

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

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

  • RemotePushDown

  • LocalizedPrepared

Правило RemotePushDown

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

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

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

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

SELECT * FROM t

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

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

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

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

  • есть соответствие между этим узлом и таблицей t (table=);

  • партицирование данных производится по хэшу колонки a (согласно схеме шардирования);

  • используется пустой фильтр.

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

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

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

SELECT * FROM t WHERE a = 1

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

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

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

  • весь план проталкивается внутрь (push down) через RemoteExec;

  • фильтрация выполняется полностью в рамках одного шарда, а по сети пересылаются уже отфильтрованные данные.

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

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

FilterExec: a@0 = 1
  RemoteExec: table=datafusion.public.t, partitioning=Hash([a@0], 1), filter=datafusion.public.t.a=1
    UnionExec
      TarantoolExec: table=datafusion.public.t fields=[a, b], ...
      BufExec: table=datafusion.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=datafusion.public.t fields=[], ...
          BufExec: table=datafusion.public.t fields=[], ...

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

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

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

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

Правило LocalizedPrepared

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

  1. Исполняется аналитический запрос (prepared statement на HTTP-адрес /computation/run).

  2. План был полностью локализован.

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

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

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

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

Found what you were looking for?
Feedback