summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ydb/docs/ru/core/_assets/episodes_scheme.pngbin0 -> 58127 bytes
-rw-r--r--ydb/docs/ru/core/_assets/explain_ui.pngbin42035 -> 47896 bytes
-rw-r--r--ydb/docs/ru/core/_assets/explain_with_index_ui.pngbin0 -> 49261 bytes
-rw-r--r--ydb/docs/ru/core/_assets/query_plan_ui.pngbin181146 -> 166742 bytes
-rw-r--r--ydb/docs/ru/core/_assets/query_plan_with_index_ui.pngbin0 -> 184138 bytes
-rw-r--r--ydb/docs/ru/core/operations/query_plan.md143
-rw-r--r--ydb/docs/ru/core/operations/query_plans_optimization.md101
-rw-r--r--ydb/docs/ru/core/operations/toc_i.yaml5
-rw-r--r--ydb/docs/ru/core/reference/ydb-cli/commands/_includes/explain-plan/examples-exp.md1
-rw-r--r--ydb/docs/ru/core/reference/ydb-cli/commands/_includes/explain-plan/examples.md11
-rw-r--r--ydb/docs/ru/core/reference/ydb-cli/commands/_includes/explain-plan/intro-exp.md20
-rw-r--r--ydb/docs/ru/core/reference/ydb-cli/commands/_includes/explain-plan/intro.md11
-rw-r--r--ydb/docs/ru/core/reference/ydb-cli/commands/explain-plan.md75
-rw-r--r--ydb/docs/ru/core/yql/_assets/connection.pngbin0 -> 9042 bytes
-rw-r--r--ydb/docs/ru/core/yql/_assets/precompute.pngbin0 -> 9125 bytes
-rw-r--r--ydb/docs/ru/core/yql/_assets/resultset.pngbin0 -> 8044 bytes
-rw-r--r--ydb/docs/ru/core/yql/_assets/stage.pngbin0 -> 6024 bytes
-rw-r--r--ydb/docs/ru/core/yql/query_plans.md164
-rw-r--r--ydb/docs/ru/core/yql/toc_i.yaml4
19 files changed, 324 insertions, 211 deletions
diff --git a/ydb/docs/ru/core/_assets/episodes_scheme.png b/ydb/docs/ru/core/_assets/episodes_scheme.png
new file mode 100644
index 00000000000..9d66abc3ce8
--- /dev/null
+++ b/ydb/docs/ru/core/_assets/episodes_scheme.png
Binary files differ
diff --git a/ydb/docs/ru/core/_assets/explain_ui.png b/ydb/docs/ru/core/_assets/explain_ui.png
index 7ab9a4f699e..a6fc95b1d52 100644
--- a/ydb/docs/ru/core/_assets/explain_ui.png
+++ b/ydb/docs/ru/core/_assets/explain_ui.png
Binary files differ
diff --git a/ydb/docs/ru/core/_assets/explain_with_index_ui.png b/ydb/docs/ru/core/_assets/explain_with_index_ui.png
new file mode 100644
index 00000000000..df2cebf182c
--- /dev/null
+++ b/ydb/docs/ru/core/_assets/explain_with_index_ui.png
Binary files differ
diff --git a/ydb/docs/ru/core/_assets/query_plan_ui.png b/ydb/docs/ru/core/_assets/query_plan_ui.png
index bfb0a562d5b..2d6164cb1ce 100644
--- a/ydb/docs/ru/core/_assets/query_plan_ui.png
+++ b/ydb/docs/ru/core/_assets/query_plan_ui.png
Binary files differ
diff --git a/ydb/docs/ru/core/_assets/query_plan_with_index_ui.png b/ydb/docs/ru/core/_assets/query_plan_with_index_ui.png
new file mode 100644
index 00000000000..19588ef56ea
--- /dev/null
+++ b/ydb/docs/ru/core/_assets/query_plan_with_index_ui.png
Binary files differ
diff --git a/ydb/docs/ru/core/operations/query_plan.md b/ydb/docs/ru/core/operations/query_plan.md
deleted file mode 100644
index 3a14dd7a129..00000000000
--- a/ydb/docs/ru/core/operations/query_plan.md
+++ /dev/null
@@ -1,143 +0,0 @@
-# Построение планов запросов
-
-Чтобы лучше понимать работу запросов, вы можете получить и проанализировать план запроса.
-Структура плана запроса в {{ ydb-short-name }} представляет собой граф. Каждый узел содержит информацию об операциях и таблицах.
-
-Тип операции | Аттрибуты | Комментарий
---- | --- | ---
-`TableFullScan` | `ReadColumns` - читаемые колонки | полное сканирование таблицы
-`TableRangeScan` | `ReadColumns` - читаемые колонки<br>`ReadRange` - диапазон ключей, по которому выполняется чтение | чтение определенного диапазона записей
-`TablePointLookup` | `ReadColumns` - читаемые колонки<br>`ReadRange` - диапазон или точка, по которым выполняется чтение | чтение по ключу или префиксу ключа
-`Upsert` | `Columns` - колонки, которые содержит добавляемая строка | добавление строк в таблицу
-`Delete` | | удаление строк из таблицы
-`Join` | в описании операции указана используемая стратегия join'а: <br>`Inner`<br>`Left`<br>`LeftOnly`<br>`Right`<br>`RightOnly`<br>`LeftSemi`<br>`RightSemi`<br>`Full`<br>`Cross` | объединение двух таблиц на основе выбранной стратегии
-`Filter` | `Predicate` - условие фильтрации<br>`Limit` - лимит на число строк | фильтрация строк
-`Aggregate` | `GroupBy` - колонки, по которым выполняется агрегирование<br>`Aggregation`- условие агрегирования | агрегирование строк
-`Sort` | `SortBy` - колонки, по которым выполняется сортировка | сортировка результата
-`TopSort` | `TopSortBy` - колонки, по которым выполняется сортировка<br>`Limit` - лимит на число строк | сортировка результата с применением лимита
-`Limit` | | величина лимита на число строк
-`Offset` | | величина смещения
-`Union` | | объединение результатов двух запросов в один результирующий набор
-
-{% list tabs %}
-
-- {{ ydb-short-name }} CLI
-
- Получить план запроса через {{ ydb-short-name }} CLI можно с помощью команды `explain`:
- ```
- ydb -e grpcs://<эндпоинт> -d <база данных> table query explain \
- -q "SELECT season_id, episode_id, title
- FROM episodes
- WHERE series_id = 1
- AND season_id > 1
- ORDER BY season_id, episode_id
- LIMIT 3"
- ```
-
- Полученный результат:
- ```
- Query Plan:
- ResultSet
- └──Limit (Limit: 3)
- └──<Merge>
- └──TopSort (Limit: 3, TopSortBy: )
- └──Filter (Predicate: Exist(item.season_id) And Exist(item.series_id))
- └──TablePointLookup (ReadRange: [series_id (1), season_id (1, +∞), episode_id (-∞, +∞)], ReadColumns: [episode_id, season_id, title, series_id], Table: episodes)
- Tables: [episodes]
- ```
-
- Дополнительно к плану запроса вы можете получить AST (абстрактное синтаксическое дерево).
- Раздел AST содержит представление на внутреннем языке miniKQL.
-
- Для получения AST необходимо вызвать `explain` с флагом `--ast`:
- ```
- ydb -e grpcs://<эндпоинт> -d <база данных> table query explain \
- -q "SELECT season_id, episode_id, title
- FROM episodes
- WHERE series_id = 1
- AND season_id > 1
- ORDER BY season_id, episode_id
- LIMIT 3" --ast
- ```
-
- Полученный AST:
- ```
- Query AST:
- (
- (let $1 (KqpTable '"episodes" '"72075186224045943:83859" '"" '1))
- (let $2 '('"episode_id" '"season_id" '"title" '"series_id"))
- (let $3 (Uint64 '1))
- (let $4 (KqpRowsSourceSettings $1 $2 '() '((KqlKeyExc $3 $3) (KqlKeyInc $3))))
- (let $5 (Uint64 '"3"))
- (let $6 (DqPhyStage '((DqSource (DataSource '"KqpReadRangesSource") $4)) (lambda '($12) (block '(
- (let $13 (Bool 'true))
- (return (FromFlow (TopSort (OrderedMap (OrderedFilter (ToFlow $12) (lambda '($14) (And (Exists (Member $14 '"season_id")) (Exists (Member $14 '"series_id"))))) (lambda '($15) (AsStruct '('"episode_id" (Member $15 '"episode_id")) '('"season_id" (Member $15 '"season_id")) '('"title" (Member $15 '"title"))))) $5 '($13 $13) (lambda '($16) '((Member $16 '"season_id") (Member $16 '"episode_id"))))))
- ))) '('('"_logical_id" '842) '('"_id" '"14388682-e03b28dd-60f91f84-d7cd002f"))))
- (let $7 (DqCnMerge (TDqOutput $6 '0) '('('"season_id" '"Asc") '('"episode_id" '"Asc"))))
- (let $8 (DqPhyStage '($7) (lambda '($17) (FromFlow (Take (ToFlow $17) $5))) '('('"_logical_id" '855) '('"_id" '"295c0459-34d4b026-b467e71f-35402430"))))
- (let $9 '('"season_id" '"episode_id" '"title"))
- (let $10 (DqCnResult (TDqOutput $8 '0) $9))
- (let $11 (OptionalType (DataType 'Uint64)))
- (return (KqpPhysicalQuery '((KqpPhysicalTx '($6 $8) '($10) '() '('('"type" '"data")))) '((KqpTxResultBinding (ListType (StructType '('"episode_id" $11) '('"season_id" $11) '('"title" (OptionalType (DataType 'String))))) '0 '0)) '('('"type" '"data_query"))))
- )
- ```
-
-- Embedded UI
-
- План запроса также можно получить через [Embedded UI](../maintenance/embedded_monitoring/ydb_monitoring.md). Для этого необходимо перейти на страницу бд в раздел `Query` и нажать `Explain`:
-
- ![explain_ui](../_assets/explain_ui.png)
-
- Полученный результат:
-
- ![query_plan_ui](../_assets/query_plan_ui.png)
-
-{% endlist %}
-
-# Использование планов при оптимизации запросов
-
-Рассмотрим запрос, выполняющий поиск серии по названию
-
-``` sql
-SELECT season_id, episode_id
- FROM episodes
- WHERE title = 'The Work Outing'
-```
-```
-Query Plan:
-ResultSet
-└──Limit (Limit: 1001)
- └──<UnionAll>
- └──Limit (Limit: 1001)
- └──Filter (Predicate: item.title == "The Work Outing")
- └──TableFullScan (ReadRanges: ["series_id (-∞, +∞)","season_id (-∞, +∞)","episode_id (-∞, +∞)"], ReadColumns: ["episode_id","season_id","title"], Table: episodes)
- Tables: ["episodes"]
-```
-
-По плану видно, что выполнение запроса приводит к полному сканированию таблицы.
-Попробуем оптимизировать запрос посредством добавления вторичного индекса для колонки `title`.
-
-``` sql
-ALTER TABLE episodes
- ADD INDEX title_index GLOBAL ON (title)
-```
-
-Построим план того же запроса с использованием вторичного индекса `title_index`, обратите вминание, что вторичный индекс надо явно указать в запросе через конструкцию `VIEW` (подробнее про вторичные индексы можно прочитать [здесь](../best_practices/_includes/secondary_indexes.md)).
-
-``` sql
-SELECT season_id, episode_id
- FROM episodes VIEW title_index
- WHERE title = 'The Work Outing'
-```
-```
-Query Plan:
-ResultSet
-└──Limit (Limit: 1001)
- └──<UnionAll>
- └──Limit (Limit: 1001)
- └──Filter (Predicate: Exist(item.title))
- └──TablePointLookup (ReadRange: ["title (The Work Outing)","series_id (-∞, +∞)","season_id (-∞, +∞)","episode_id (-∞, +∞)"], ReadLimit: 1001, ReadColumns: ["episode_id","season_id","title"], Table: episodes/title_index/indexImplTable)
- Tables: ["episodes/title_index/indexImplTable"]
-```
-
-С использованием вторичного индекса запрос выполняется оптимально через чтение индексной таблицы по ключу. \ No newline at end of file
diff --git a/ydb/docs/ru/core/operations/query_plans_optimization.md b/ydb/docs/ru/core/operations/query_plans_optimization.md
new file mode 100644
index 00000000000..7bba702ea5a
--- /dev/null
+++ b/ydb/docs/ru/core/operations/query_plans_optimization.md
@@ -0,0 +1,101 @@
+# Использование планов при оптимизации запросов
+
+При написании запроса важно проанализировать план его выполнения для своевременного обнаружения и устранения причин возможного чрезмерного потребления ресурсов кластера или аномально высокого времени выполнения. В данной статье будет рассмотрен конкретный пример анализа плана запроса, а со справочной информацией о структуре планов {{ ydb-short-name }} можно ознакомиться [здесь](../yql/query_plans.md).
+
+Рассмотрим следующий запрос, выполняющий поиск серии по названию:
+
+``` sql
+SELECT season_id, episode_id
+ FROM episodes
+ WHERE title = 'The Work Outing'
+```
+
+Схема таблицы `episodes`:
+
+![episodes](../_assets/episodes_scheme.png)
+
+Построим план для данного запроса, в {{ ydb-short-name }} это можно сделать двумя способами:
+
+{% list tabs %}
+
+- {{ ydb-short-name }} CLI
+
+ Получить план запроса через {{ ydb-short-name }} [CLI](../reference/ydb-cli/_includes/index.md) можно с помощью следующей команды:
+ ```
+ ydb -p <profile_name> table query explain \
+ -q "SELECT season_id, episode_id
+ FROM episodes
+ WHERE title = 'The Work Outing'"
+ ```
+
+ Результат:
+ ```
+ Query Plan:
+ ResultSet
+ └──Limit (Limit: 1001)
+ └──<UnionAll>
+ └──Limit (Limit: 1001)
+ └──Filter (Predicate: item.title == "The Work Outing")
+ └──TableFullScan (ReadRanges: ["series_id (-∞, +∞)","season_id (-∞, +∞)","episode_id (-∞, +∞)"], ReadColumns: ["episode_id","season_id","title"], Table: episodes)
+ Tables: ["episodes"]
+ ```
+
+- Embedded UI
+
+ План запроса также можно получить через [Embedded UI](../maintenance/embedded_monitoring/ydb_monitoring.md). Для этого необходимо перейти на страницу базы данных в раздел `Query` и нажать `Explain`:
+
+ ![explain_ui](../_assets/explain_ui.png)
+
+ Результат:
+
+ ![query_plan_ui](../_assets/query_plan_ui.png)
+
+{% endlist %}
+
+И в визуальном и в текстовом представлении видно, что в корне этого плана возвращение данных на клиент, в листьях работа с таблицами, а на промежуточных узлах — преобразования данных. Важно обратить внимание на узел, показывающий обращение к таблице `episodes`. В данном случае это `TableFullScan`, который означает выполнение полного сканирования таблицы. А полное сканирование таблицы потребляет времени и ресурсов пропорционально её размеру, из-за чего по возможности их стараются избегать в таблицах, которые имеют тенденцию расти с течением времени или просто большие.
+
+Одним из типовых способов избежать полного сканирования таблицы является добавление [вторичного индекса](../best_practices/_includes/secondary_indexes.md). В данном случае имеет смысл добавить вторичный индекс для колонки `title`, для этого воспользуемся запросом:
+
+``` sql
+ALTER TABLE episodes
+ ADD INDEX title_index GLOBAL ON (title)
+```
+
+Стоит отметить, что в данном примере мы используем [синхронный вторичный индекс](../concepts/_includes/secondary_indexes.md#sync). Создание индекса в {{ ydb-short-name }} — асинхронная операция, поэтому даже если запрос на построение завершился успехом, стоит подождать какое-то время, так как фактически индекс может быть еще не готов к использованию. Управлять асинхронной операцией можно через [CLI](../reference/ydb-cli/commands/_includes/secondary_index.md#add).
+
+Построим план того же запроса с использованием вторичного индекса `title_index`. Обратите внимание, что вторичный индекс надо явно указать в запросе через конструкцию `VIEW`.
+
+{% list tabs %}
+
+- {{ ydb-short-name }} CLI
+
+ Команда:
+ ```
+ ydb -p <profile_name> table query explain \
+ -q "SELECT season_id, episode_id
+ FROM episodes VIEW title_index
+ WHERE title = 'The Work Outing'"
+ ```
+
+ Результат:
+ ```
+ Query Plan:
+ ResultSet
+ └──Limit (Limit: 1001)
+ └──<UnionAll>
+ └──Limit (Limit: 1001)
+ └──Filter (Predicate: Exist(item.title))
+ └──TablePointLookup (ReadRange: ["title (The Work Outing)","series_id (-∞, +∞)","season_id (-∞, +∞)","episode_id (-∞, +∞)"], ReadLimit: 1001, ReadColumns: ["episode_id","season_id","title"], Table: episodes/title_index/indexImplTable)
+ Tables: ["episodes/title_index/indexImplTable"]
+ ```
+- Embedded UI
+
+ ![explain_ui](../_assets/explain_with_index_ui.png)
+
+ Результат:
+
+ ![query_plan_ui](../_assets/query_plan_with_index_ui.png)
+
+{% endlist %}
+
+С использованием вторичного индекса запрос выполняется без полного сканирования основной таблицы. Вместо `TableFullScan` появился `TablePointLookup` - чтение индексной таблицы по ключу, а основную таблицу мы теперь совсем не читаем, так как все нужные нам колонки содержатся в индексной таблице. \ No newline at end of file
diff --git a/ydb/docs/ru/core/operations/toc_i.yaml b/ydb/docs/ru/core/operations/toc_i.yaml
index 2f5fe5e038b..1d5ad668c08 100644
--- a/ydb/docs/ru/core/operations/toc_i.yaml
+++ b/ydb/docs/ru/core/operations/toc_i.yaml
@@ -4,6 +4,5 @@ items:
hidden: true
- name: Пользовательские атрибуты таблицы
href: manage-users-attr.md
-- name: Построение планов запросов
- href: query_plan.md
- hidden: true
+- name: Использование планов при оптимизации запросов
+ href: query_plans_optimization.md
diff --git a/ydb/docs/ru/core/reference/ydb-cli/commands/_includes/explain-plan/examples-exp.md b/ydb/docs/ru/core/reference/ydb-cli/commands/_includes/explain-plan/examples-exp.md
deleted file mode 100644
index ae005534ec2..00000000000
--- a/ydb/docs/ru/core/reference/ydb-cli/commands/_includes/explain-plan/examples-exp.md
+++ /dev/null
@@ -1 +0,0 @@
-Из данного плана запроса следует, что для таблицы `seasons` будет выполнен `FullScan`, а для таблицы `series` — множественные чтения (тип обращения `MultiLookup`) по ключу `series_id` (lookup_by). А тип чтения `MultiLookup` и раздел `lookup_by` говорят о том, что для таблицы `series` будут выполнены множественные чтения по ключу `series_id`.
diff --git a/ydb/docs/ru/core/reference/ydb-cli/commands/_includes/explain-plan/examples.md b/ydb/docs/ru/core/reference/ydb-cli/commands/_includes/explain-plan/examples.md
deleted file mode 100644
index 5eaf9a75e20..00000000000
--- a/ydb/docs/ru/core/reference/ydb-cli/commands/_includes/explain-plan/examples.md
+++ /dev/null
@@ -1,11 +0,0 @@
-## Пример модификации запроса {#examples}
-
-Измените запрос так, чтобы получить только первые сезоны всех сериалов:
-
-```bash
-{{ ydb-cli }} table query explain \
- -q "SELECT sa.title AS season_title, sr.title AS series_title, sr.series_id, sa.season_id
- FROM seasons AS sa
- INNER JOIN series AS sr ON sa.series_id = sr.series_id
- WHERE sa.season_id = 1"
-```
diff --git a/ydb/docs/ru/core/reference/ydb-cli/commands/_includes/explain-plan/intro-exp.md b/ydb/docs/ru/core/reference/ydb-cli/commands/_includes/explain-plan/intro-exp.md
deleted file mode 100644
index 6aabe9b82d0..00000000000
--- a/ydb/docs/ru/core/reference/ydb-cli/commands/_includes/explain-plan/intro-exp.md
+++ /dev/null
@@ -1,20 +0,0 @@
-Основная секция плана запроса, `tables`, содержит информацию об обращениях к таблицам. Чтения описаны в разделе `reads`, а записи — в разделе `writes`. Ключевой характеристикой любого обращения к таблице является его тип.
-
-Типы чтения:
-
-* `FullScan` — полное сканирование таблицы. Читаются все записи на всех шардах.
-* `Scan` — чтение определенного диапазона записей.
-* `Lookup` — чтение по ключу или префиксу ключа.
-* `MultiLookup` — множественные чтения по ключу или префиксу ключа. Данный тип обращения возможен, например, в JOIN'ах.
-
-Типы записи:
-
-* `Upsert` — добавление одной записи.
-* `MultiUpsert` — добавление нескольких записей.
-* `Erase` — единичное удаление по ключу.
-* `MultiErase` — множественное удаление.
-
-Рассмотрим план запроса из примера выше.
-Параметр `lookup_by` показывает, по каким колонкам (ключу или префиксу ключа) выполняется чтение.
-Параметр `scan_by` показывает, по каким колонкам выполняется чтение всех записей в определенном диапазоне значений.
-В `columns` перечислены колонки, значения которых будут вычитываться из таблицы.
diff --git a/ydb/docs/ru/core/reference/ydb-cli/commands/_includes/explain-plan/intro.md b/ydb/docs/ru/core/reference/ydb-cli/commands/_includes/explain-plan/intro.md
deleted file mode 100644
index 1cc9dd97819..00000000000
--- a/ydb/docs/ru/core/reference/ydb-cli/commands/_includes/explain-plan/intro.md
+++ /dev/null
@@ -1,11 +0,0 @@
-Получите план запроса:
-
-```bash
-{{ ydb-cli }} table query explain \
- -q "SELECT season_id, episode_id, title
- FROM episodes
- WHERE series_id = 1
- AND season_id > 1
- ORDER BY season_id, episode_id
- LIMIT 3"
-```
diff --git a/ydb/docs/ru/core/reference/ydb-cli/commands/explain-plan.md b/ydb/docs/ru/core/reference/ydb-cli/commands/explain-plan.md
index b5e667ef014..c02ab2f7539 100644
--- a/ydb/docs/ru/core/reference/ydb-cli/commands/explain-plan.md
+++ b/ydb/docs/ru/core/reference/ydb-cli/commands/explain-plan.md
@@ -1,27 +1,60 @@
# Получение плана исполнения запроса и AST
-Чтобы лучше понимать работу запросов, вы можете получить и проанализировать план запроса. Дополнительно к плану запроса вы можете получить AST (абстрактное синтаксическое дерево). Раздел AST содержит представление на внутреннем языке miniKQL.
+Получить план запроса через {{ ydb-short-name }} CLI можно с помощью команды `explain`:
+ ```
+ ydb -p <profile_name> table query explain \
+ -q "SELECT season_id, episode_id, title
+ FROM episodes
+ WHERE series_id = 1
+ AND season_id > 1
+ ORDER BY season_id, episode_id
+ LIMIT 3"
+ ```
-## Получите плана запроса {#explain-plan}
+Полученный результат:
+ ```
+ Query Plan:
+ ResultSet
+ └──Limit (Limit: 3)
+ └──<Merge>
+ └──TopSort (Limit: 3, TopSortBy: )
+ └──Filter (Predicate: Exist(item.season_id) And Exist(item.series_id))
+ └──TablePointLookup (ReadRange: [series_id (1), season_id (1, +∞), episode_id (-∞, +∞)], ReadColumns: [episode_id, season_id, title, series_id], Table: episodes)
+ Tables: [episodes]
+ ```
-{% include [intro.md](_includes/explain-plan/intro.md) %}
+Дополнительно к плану запроса вы можете получить AST (абстрактное синтаксическое дерево).
+Раздел AST содержит представление на внутреннем языке miniKQL.
-{% include [intro-exp.md](_includes/explain-plan/intro-exp.md) %}
+Для получения AST необходимо вызвать `explain` с флагом `--ast`:
+ ```
+ ydb -p <profile_name> table query explain \
+ -q "SELECT season_id, episode_id, title
+ FROM episodes
+ WHERE series_id = 1
+ AND season_id > 1
+ ORDER BY season_id, episode_id
+ LIMIT 3" --ast
+ ```
-## Получите AST {#ast}
-
-Для получения AST добавьте в конце команды флаг `--ast`:
-
-```bash
-ydb -e grpcs://<эндпоинт> -d <база данных> \
-table query explain -q "SELECT season_id, episode_id, title \
-FROM episodes \
-WHERE series_id = 1 AND season_id > 1 \
-ORDER BY season_id, episode_id LIMIT 3" --ast
-```
-
-В результате дополнительно отобразится AST запроса.
-
-{% include [examples.md](_includes/explain-plan/examples.md) %}
-
-{% include [examples-exp.md](_includes/explain-plan/examples-exp.md) %}
+Полученный AST:
+ ```
+ Query AST:
+ (
+ (let $1 (KqpTable '"episodes" '"72075186224045943:83859" '"" '1))
+ (let $2 '('"episode_id" '"season_id" '"title" '"series_id"))
+ (let $3 (Uint64 '1))
+ (let $4 (KqpRowsSourceSettings $1 $2 '() '((KqlKeyExc $3 $3) (KqlKeyInc $3))))
+ (let $5 (Uint64 '"3"))
+ (let $6 (DqPhyStage '((DqSource (DataSource '"KqpReadRangesSource") $4)) (lambda '($12) (block '(
+ (let $13 (Bool 'true))
+ (return (FromFlow (TopSort (OrderedMap (OrderedFilter (ToFlow $12) (lambda '($14) (And (Exists (Member $14 '"season_id")) (Exists (Member $14 '"series_id"))))) (lambda '($15) (AsStruct '('"episode_id" (Member $15 '"episode_id")) '('"season_id" (Member $15 '"season_id")) '('"title" (Member $15 '"title"))))) $5 '($13 $13) (lambda '($16) '((Member $16 '"season_id") (Member $16 '"episode_id"))))))
+ ))) '('('"_logical_id" '842) '('"_id" '"14388682-e03b28dd-60f91f84-d7cd002f"))))
+ (let $7 (DqCnMerge (TDqOutput $6 '0) '('('"season_id" '"Asc") '('"episode_id" '"Asc"))))
+ (let $8 (DqPhyStage '($7) (lambda '($17) (FromFlow (Take (ToFlow $17) $5))) '('('"_logical_id" '855) '('"_id" '"295c0459-34d4b026-b467e71f-35402430"))))
+ (let $9 '('"season_id" '"episode_id" '"title"))
+ (let $10 (DqCnResult (TDqOutput $8 '0) $9))
+ (let $11 (OptionalType (DataType 'Uint64)))
+ (return (KqpPhysicalQuery '((KqpPhysicalTx '($6 $8) '($10) '() '('('"type" '"data")))) '((KqpTxResultBinding (ListType (StructType '('"episode_id" $11) '('"season_id" $11) '('"title" (OptionalType (DataType 'String))))) '0 '0)) '('('"type" '"data_query"))))
+ )
+ ```
diff --git a/ydb/docs/ru/core/yql/_assets/connection.png b/ydb/docs/ru/core/yql/_assets/connection.png
new file mode 100644
index 00000000000..06c1f46c6e4
--- /dev/null
+++ b/ydb/docs/ru/core/yql/_assets/connection.png
Binary files differ
diff --git a/ydb/docs/ru/core/yql/_assets/precompute.png b/ydb/docs/ru/core/yql/_assets/precompute.png
new file mode 100644
index 00000000000..ae1e85d4e19
--- /dev/null
+++ b/ydb/docs/ru/core/yql/_assets/precompute.png
Binary files differ
diff --git a/ydb/docs/ru/core/yql/_assets/resultset.png b/ydb/docs/ru/core/yql/_assets/resultset.png
new file mode 100644
index 00000000000..65d5bcb3bd1
--- /dev/null
+++ b/ydb/docs/ru/core/yql/_assets/resultset.png
Binary files differ
diff --git a/ydb/docs/ru/core/yql/_assets/stage.png b/ydb/docs/ru/core/yql/_assets/stage.png
new file mode 100644
index 00000000000..5abf955426a
--- /dev/null
+++ b/ydb/docs/ru/core/yql/_assets/stage.png
Binary files differ
diff --git a/ydb/docs/ru/core/yql/query_plans.md b/ydb/docs/ru/core/yql/query_plans.md
new file mode 100644
index 00000000000..1c22e2b7b27
--- /dev/null
+++ b/ydb/docs/ru/core/yql/query_plans.md
@@ -0,0 +1,164 @@
+# Структура планов запросов
+
+Чтобы лучше понимать работу запроса, вы можете получить и проанализировать его план.
+Структура плана запроса в {{ ydb-short-name }} представляет собой граф, где каждый узел содержит информацию об операциях и таблицах.
+
+Ниже представлена справочная информация по типам узлов, а пример анализа конкретного плана запроса можно посмотреть [здесь](../operations/query_plans_optimization.md).
+
+## Типы узлов
+### Stage
+Стадия выполнения запроса.
+
+**Визуальное представление**:
+![stage](_assets/stage.png)
+
+Стадия может содержать следующие операции:
+
+#### TableFullScan
+Полное сканирование таблицы. Важно иметь ввиду, что ресурсоёмкость данной операции пропорциональна размеру таблицы, поэтому по возможности ее стоит избегать.
+
+Атрибут | Значение
+--- | ---
+Table | имя таблицы
+ReadColumns | список читаемых колонок
+ReadLimit | лимит на число прочитанных строк
+Reverse | флаг, указывающий на порядок, в котором будут прочитаны строки, по умолчанию порядок прямой (от меньшего к большему), но в случае выставления флага в `true` порядок чтения будет обратным
+
+#### TableRangeScan
+Чтение строк таблицы по определённому диапазону значений первичного ключа.
+
+Атрибут | Значение
+--- | ---
+Table | имя таблицы
+ReadColumns | список читаемых колонок
+ReadRange | диапазон ключей, по которому выполняется чтение
+ReadLimit | лимит на число прочитанных строк
+Reverse | флаг, указывающий на порядок, в котором будут прочитаны строки, по умолчанию порядок прямой (от меньшего к большему), но в случае выставления флага в `true` порядок чтения будет обратным
+
+#### TablePointLookup
+Чтение строк таблицы по конкретным значениям первичного ключа. Обратите внимание, что для этой операции необходимо указать все компоненты первичного ключа, чтение по префиксу ключа выполняется как `TableRangeScan`.
+
+Атрибут | Значение
+--- | ---
+Table | имя таблицы
+ReadColumns | список читаемых колонок
+
+#### Upsert
+Перезапись строк таблицы по соответствующим значениям первичного ключа, если они существовали, иначе добавление новых строк.
+
+Атрибут | Значение
+--- | ---
+Table | имя таблицы
+Columns | колонки, которые содержит строка
+
+#### Delete
+Удаление строк из таблицы.
+
+Атрибут | Значение
+--- | ---
+Table | имя таблицы
+
+#### Join
+Объединение двух таблиц, в описании операции указана используемая стратегия (JoinDict или MapJoin).
+
+- JoinDict - строятся словари для правой и левой частей, а затем выполняется их объединение по ключам
+- MapJoin - для каждого элемента из левой части выполняется поиск по ключу в предварительно построенном словаре для правой части
+
+#### Filter
+Фильтрация строк, в результате остаются только те строки, для которых предикат оказался true.
+
+Атрибут | Значение
+--- | ---
+Predicate | условие фильтрации
+Limit | лимит на число строк
+
+#### Aggregate
+Группировка строк по значениям колонок с построением агрегатов.
+
+Атрибут | Значение
+--- | ---
+GroupBy | колонки, по которым выполняется группировка
+Aggregation | агрегатные функции
+
+#### Sort
+Сортировка строк.
+
+Атрибут | Значение
+--- | ---
+SortBy | колонки, по которым выполняется сортировка
+
+#### TopSort
+Сортировка строк с применением лимита.
+
+Атрибут | Значение
+--- | ---
+TopSortBy | колонки, по которым выполняется сортировка
+Limit | лимит на число строк
+
+#### Top
+Взятие N элементов таким образом, что все они меньше или равны N+1 элемента, если бы вся последовательность была отсортирована.
+
+Атрибут | Значение
+--- | ---
+TopBy | колонки, по которым будут взяты первые N строк
+Limit | лимит на число строк
+
+#### Limit
+Ограничение на число строк.
+
+Атрибут | Значение
+--- | ---
+Limit | величина лимита
+
+#### Offset
+Смещение, позволяющее пропустить первые N элементов заданного набора строк.
+
+Атрибут | Значение
+--- | ---
+Offset | величина смещения
+
+#### Union
+Объединение результатов двух и более подзапросов в один набор.
+
+#### Iterator
+Итератор по заданному набору строк, как правило в качестве аргумента принимает [precompute](#precompute).
+
+#### PartitionByKey
+Партиционирование по ключу, как правило в качестве аргумента принимает [precompute](#precompute).
+
+### Connection
+Зависимость по данным между стадиями.
+
+**Визуальное представление**:
+![connection](_assets/connection.png)
+
+Каждая стадия исполняется в виде некоторого количества тасков. Например, читающая таблицу стадия может исполняться в N тасков, где N - это количество шардов таблицы. От того, какой connection используется, зависит как именно данные будут передаваться между стадиями. Далее будут описаны все возможные типы connection'ов, для их описания удобно ввести термины producer stage - стадия, отправляющая данные и consumer stage - стадия, принимающая данные.
+
+#### UnionAll
+Объединяет результаты всех тасков producer stage и отдаёт их как единый результат в единственный таск consumer stage.
+
+#### Merge
+Частный случай `UnionAll`, когда результаты producer stage отсортированы по заданному набору колонок, результат также отсортирован.
+
+#### Broadcast
+Рассылает результат единственного таска producer stage на все таски consumer stage.
+
+#### Map
+Реализует связи тасков стадий 1-к-1, то есть producer stage и consumer stage должны иметь одинаковое количество тасков.
+
+#### HashShuffle
+Рассылает результаты с тасков producer stage на таски consumer stage по некоторому правилу для заданных колонок. Правило жётско описано в коде, но вот список колонок указывается в каждом connection'е независимо.
+
+### ResultSet
+Результат выполнения запроса, возвращаемый клиенту.
+
+**Визуальное представление**:
+![resultset](_assets/resultset.png)
+
+### Precompute
+Материализованный в памяти промежуточный результат.
+
+**Визуальное представление**:
+![precompute](_assets/precompute.png)
+
+Cтадии, использующие precompute, начинают выполняться только после завершения вычисления precompute.
diff --git a/ydb/docs/ru/core/yql/toc_i.yaml b/ydb/docs/ru/core/yql/toc_i.yaml
index d687b770ba7..6218b7eb322 100644
--- a/ydb/docs/ru/core/yql/toc_i.yaml
+++ b/ydb/docs/ru/core/yql/toc_i.yaml
@@ -5,4 +5,6 @@ items:
href: ../getting_started/yql.md
- include: { mode: link, path: reference/toc_i.yaml }
- name: Туториал YQL
- include: { mode: link, path: tutorial/toc_i.yaml } \ No newline at end of file
+ include: { mode: link, path: tutorial/toc_i.yaml }
+- name: Планы запросов
+ href: query_plans.md \ No newline at end of file