Фасет RAW¶
В Picodata распределенные SQL-запросы компилируются во множество локальных
SQL-запросов, исполняющихся на узлах кластера. Фасет RAW позволяет увидеть
каждый локальный SQL-запрос, а также соответствующий ему план исполнения.
Весь вывод фасета RAW разбит на стадии, которые пронумерованы в порядке
их исполнения. В некоторых случаях стадия может быть разбита на несколько
подстадий; тогда используется составная нумерация, например, 8.2..
Помимо номера, заголовок каждой стадии включает тип и место исполнения.
Тело стадии содержит разнообразную информацию об исполняемом на этой стадии
локальном SQL-запросе.
Пример заголовка стадии:
╭──────────────────────────╮
│ 3. Query (WHOLE STORAGE) │
╰──────────────────────────╯
Возможные типы стадий:
Let query— DQL-запрос, привязанный к переменнойLET.Return query— DQL-запрос, который возвращает строки из транзакционного блока.If cond— DQL-запрос в условииIF-блока.If body— DML-запрос, входящий в телоIF-блока.Query— DML-запрос из транзакционного блока или DQL-запрос вне транзакционного блока.
Возможные места исполнения стадий:
ROUTER— запрос исполняется локально на узле-координаторе (который и называется роутером).WHOLE STORAGE— запрос исполняется на каждом репликасете в кластере.CONST-FILTERED STORAGE, N/M— статическая фильтрация репликасетов. Планировщику удалось до исполнения запроса вычислить конкретные репликасеты, на которые он будет отправлен. Также рядом указано количество выбранных репликасетов (N) и количество всех доступных репликасетов (M) в кластере.DYN-FILTERED STORAGE— динамическая фильтрация репликасетов. Репликасеты, на которые следует отправить запрос, будут определены в процессе исполнения на основе обрабатываемых данных. В некоторых случаях может быть дополнительно указано<= N/M, если перед динамической фильтрацией бакетов удалось применить статическую фильтрацию (см.CONST-FILTERED STORAGEвыше).
План исполнения локального SQL-запроса состоит из абстрактных шагов, которые последовательно осуществляют чтение, фильтрацию и соединение строк участвующих в запросе отношений. План имеет древовидную структуру, которая обозначается при помощи горизональных отступов. Каждый шаг содержит краткое описание выполняемой операции, например, "сканирование таблицы", "поиск по индексу" и прочие. Это позволяет оценить эффективность плана в первом приближении, а затем внести исправления в запрос при необходимости.
Пример плана локального SQL-запроса:
[0] SCAN TABLE testing_space (~1048576 rows)
[0] SCAN TABLE _tmp_16164961469071853974_0136 (~1048576 rows)
[0] USE TEMP B-TREE FOR GROUP BY
[0] USE TEMP B-TREE FOR ORDER BY
Взаимодействие с фасетом BUCKETS¶
При одновременном использовании фасетов RAW и BUCKETS в тело стадии
добавляется оценка бакетов, участвующих в исполнении локального SQL-запроса.
Формат вывода совпадает с обычным применением фасета BUCKETS.
Взаимодействие с ошибками при исполнении запроса¶
В случае, если пользовательский запрос не удалось выполнить, Picodata
вернет ошибку. В сообщении ошибки может быть указан номер стадии
EXPLAIN (RAW), который позволит найти конкретный локальный SQL-запрос,
который не удалось выполнить, а также другую полезную информацию.
Пример:
-- Ошибка указывает на стадию 4
ERROR: sbroad: Query 4 from EXPLAIN (RAW): ...
Примеры¶
Ниже представлены примеры вывода EXPLAIN (RAW) с объяснением.
Подготовка тестового окружения
Примеры использования команд включают в себя запросы к тестовым таблицам.
Последовательное сканирование¶
EXPLAIN (RAW) SELECT * FROM warehouse;
╭──────────────────────────╮
│ 1. Query (WHOLE STORAGE) │
╰──────────────────────────╯
SELECT "warehouse"."id", "warehouse"."item", "warehouse"."type" FROM "warehouse"
plan:
[0] SCAN TABLE warehouse (~1048576 rows)
Запрос будет разослан на все репликасеты кластера, и на каждом будет произведено
последовательное сканирование таблицы warehouse.
Сканирование индекса¶
EXPLAIN (RAW) SELECT * FROM warehouse WHERE id = 42;
╭────────────────────────────────────────╮
│ 1. Query (CONST-FILTERED STORAGE, 1/4) │
╰────────────────────────────────────────╯
SELECT "warehouse"."id", "warehouse"."item", "warehouse"."type" FROM "warehouse" WHERE "warehouse"."id" = CAST(42 AS int)
plan:
[0] SEARCH TABLE warehouse USING PRIMARY KEY (id=?) (~1 row)
Из вывода следует, что запрос исполнится на одном из четырех репликасетов. Для поиска в таблице будет использован индекс первичного ключа.
Фасет RAW также отражает информацию об использовании вторичных индексов.
Например:
EXPLAIN (RAW) SELECT * FROM warehouse WHERE item = 'kek';
╭──────────────────────────╮
│ 1. Query (WHOLE STORAGE) │
╰──────────────────────────╯
SELECT "warehouse"."id", "warehouse"."item", "warehouse"."type" FROM "warehouse" WHERE "warehouse"."item" = CAST('kek' AS string)
plan:
[0] SEARCH TABLE warehouse USING COVERING INDEX item_idx (item=?) (~10 rows)
USING ... INDEX в выводе указывает на то, что при исполнении запроса
будет задействован вторичный индекс.
Использование временной таблицы для агрегации¶
Для исполнения части SQL запросов Picodata материализует промежуточные данные во временную таблицу. Например:
EXPLAIN (RAW, FMT) SELECT * FROM warehouse ORDER BY 1;
╭──────────────────────────╮
│ 1. Query (WHOLE STORAGE) │
╰──────────────────────────╯
SELECT
"warehouse"."id",
"warehouse"."item",
"warehouse"."type"
FROM
"warehouse"
plan:
[0] SCAN TABLE warehouse (~1048576 rows)
╭───────────────────╮
│ 2. Query (ROUTER) │
╰───────────────────╯
SELECT
"COL_0" as "id",
"COL_1" as "item",
"COL_2" as "type"
FROM
(
SELECT
"COL_0",
"COL_1",
"COL_2"
FROM
"_tmp_6176347154012311129_0136"
)
ORDER BY
1
plan:
[0] SCAN TABLE _tmp_6176347154012311129_0136 (~1048576 rows)
[0] USE TEMP B-TREE FOR ORDER BY
Сначала Picodata выполнит сканирование таблицы warehouse на каждом репликасете
в кластере, а далее отсортирует вернувшиеся строки на узле-координаторе,
используя для этого временную таблицу.
Использование в транзакционных блоках¶
EXPLAIN (RAW)
DO $$ BEGIN
LET a = (SELECT id FROM foo WHERE id = 42);
IF a > 5 THEN
UPDATE foo SET val = 'kek' WHERE id = 42;
END IF;
END $$;
╭──────────────────────────────────────────╮
│ 1. Let "a" (CONST-FILTERED STORAGE, 1/4) │
╰──────────────────────────────────────────╯
SELECT "foo"."id" FROM "foo" WHERE "foo"."id" = CAST(42 AS int)
plan:
[0] SEARCH TABLE foo USING PRIMARY KEY (id=?) (~1 row)
╭──────────────────────────────────────────╮
│ 2. If cond (CONST-FILTERED STORAGE, 1/4) │
╰──────────────────────────────────────────╯
SELECT CAST(:a AS int) > CAST(5 AS int) as "cond"
plan:
[0] TRIVIAL
╭──────────────────────────────────────────╮
│ 3. If body (CONST-FILTERED STORAGE, 1/4) │
╰──────────────────────────────────────────╯
UPDATE "foo" SET "val" = CAST('kek' AS string) WHERE "foo"."id" = CAST(42 AS int)
plan:
[0] SEARCH TABLE foo USING PRIMARY KEY (id=?) (~1 row)
В случае если LET-выражение не используется, это отражается в выводе:
EXPLAIN (RAW)
DO $$ BEGIN
LET a = (SELECT 1);
LET a = (SELECT id FROM foo WHERE id = 42);
RETURN QUERY SELECT a;
END $$;
╭─────────────────────────────────────────────────────╮
│ 1. **Unused** let "a" (CONST-FILTERED STORAGE, 1/4) │
╰─────────────────────────────────────────────────────╯
SELECT CAST(1 AS int) as "col_1"
plan:
[0] TRIVIAL
╭──────────────────────────────────────────╮
│ 2. Let "a" (CONST-FILTERED STORAGE, 1/4) │
╰──────────────────────────────────────────╯
SELECT "foo"."id" FROM "foo" WHERE "foo"."id" = CAST(42 AS int)
plan:
[0] SEARCH TABLE foo USING PRIMARY KEY (id=?) (~1 row)
╭───────────────────────────────────────────────╮
│ 3. Return query (CONST-FILTERED STORAGE, 1/4) │
╰───────────────────────────────────────────────╯
SELECT CAST(:a AS int) as "col_1"
plan:
[0] TRIVIAL
Слово **Unused** рядом с именем LET-выражения свидетельствует о том,
что его значение никак не используется в транзакционном блоке. Таким
образом, EXPLAIN (RAW) можно использовать для поиска "мертвого кода".