diff --git a/yt/docs/en/user-guide/dynamic-tables/secondary-indices.md b/yt/docs/en/user-guide/dynamic-tables/secondary-indices.md new file mode 100644 index 000000000000..a17a35b76206 --- /dev/null +++ b/yt/docs/en/user-guide/dynamic-tables/secondary-indices.md @@ -0,0 +1 @@ +{% include [No translation note](../../_includes/no-translation.md) %} diff --git a/yt/docs/ru/_includes/user-guide/dynamic-tables/dyn-query-language.md b/yt/docs/ru/_includes/user-guide/dynamic-tables/dyn-query-language.md index 30a59c8e9290..275f9f8e4a33 100644 --- a/yt/docs/ru/_includes/user-guide/dynamic-tables/dyn-query-language.md +++ b/yt/docs/ru/_includes/user-guide/dynamic-tables/dyn-query-language.md @@ -63,7 +63,7 @@ FROM [//path/to/table] [[AS] ] [WITH INDEX [//path/to/index/table]] Определяет источники данных для запроса. Таблица, указываемая сразу после FROM, считается основной, и она используется при координации исполнения запроса (если в запросе нет ключевого слова WITH INDEX). Также допустимо указание вспомогательных таблиц-справочников в секции JOIN. Таблицы-справочники подключаются к основной таблице при точном совпадении указанного ключа. Без указания ключевого слова LEFT будет выполнен INNER JOIN. Дополнительно можно указывать выражение для фильтрации таблицы справочника, которое будет выполняться при чтении данных до выполнения join с основной таблицей. -При наличии в запросе ключевого слова WITH INDEX, индексная таблица будет использоваться для координации, а таблица указанная после FROM будет подключена к ней по общим колонкам. См. также [вторичные индексы](../../../user-guide/dynamic-tables/secondary-indices). +При наличии в запросе ключевого слова WITH INDEX, индексная таблица будет использоваться для координации, а таблица, указанная после FROM, будет подключена к ней по общим колонкам. См. также [вторичные индексы](../../../user-guide/dynamic-tables/secondary-indices). Для объединения таблиц по нескольким колонкам в условии ON необходимо записать кортеж. diff --git a/yt/docs/ru/_includes/user-guide/dynamic-tables/secondary-indices.md b/yt/docs/ru/_includes/user-guide/dynamic-tables/secondary-indices.md index f67324646523..b545efe9952d 100644 --- a/yt/docs/ru/_includes/user-guide/dynamic-tables/secondary-indices.md +++ b/yt/docs/ru/_includes/user-guide/dynamic-tables/secondary-indices.md @@ -2,7 +2,7 @@ В теории баз данных вторичным индексом называется структура данных, позволяющая ускорить выборку из таблицы по неключевым колонкам. Индексируемый кортеж неключевых колонок называется *вторичным ключем*. -В {{product-name}} такие структуры реализованы для сортированных динамических таблиц в виде отдельных таблиц, называемых далее индексными, и специальных объектов, связывающих индексируемую таблицу с индексной. Эти объекты в терминологии {{product-name}} и называются вторичными индексами. Они не имеют собственных путей в Кипарисе, являются неизменяемыми и существуют пока существуют обе индексируемая и индексная таблицы. +В {{product-name}} такие структуры реализованы для сортированных динамических таблиц в виде отдельных таблиц, называемых далее индексными, и специальных объектов, связывающих индексируемую таблицу с индексной. Эти объекты в терминологии {{product-name}} и называются вторичными индексами. Они не имеют собственных путей в Кипарисе, являются неизменяемыми и существуют, пока существуют обе индексируемая и индексная таблицы. Вторичные индексы поддержаны также для реплицированных таблиц. @@ -11,14 +11,16 @@ {% if audience == "internal" %} {% note warning %} -Ввиду того, что вторичные индексы мало протестированы, на данный момент они доступны всем желающим только на тестовых кластерах Zeno, Hume, Freud, Pythia. Если вы хотите использовать их в продакшн-процессах, напишите, пожалуйста, на yt-admin@ описание подробностей вашего процесса. +{{page.secondary-indices.usage-note}} {% endnote %} {% endif %} -По умолчанию индексация происходит по кортежу из одной или нескольких неключевых колонок таблицы, а между строками таблиц имеет место биективное соответствие - такое поведение задается атрибутом вторичного индекса `kind=full_sync`. +По умолчанию индексация происходит по кортежу из одной или нескольких неключевых колонок таблицы, а между строками таблиц имеет место биективное соответствие — такое поведение задается атрибутом вторичного индекса `kind=full_sync`. -То, какие выборки индекс способен ускорить, определяется схемой индексной таблицы. Для эффективного выбора диапазона чтения необходимо, чтобы вторичный ключ являлся префиксом ключа индексной таблицы. Быстрый доступ до данных в индексируемой таблице достигается за счет хранения полного её ключа (называемого *первичным*) в индексной таблице. Поскольку строки сортированных динамических таблиц должны быть уникальными по ключу, то ключ индексной таблицы должен содержать первичный ключ, то есть представлять собой конкатенацию вторичного и первичного ключей (у этого правила есть [исключение](../../../user-guide/dynamic-tables/secondary-indices#unique)). В качестве неключевых колонок индексная таблица может содержать одну или несколько неключевых колонок индексируемой таблицы. Она также может не содержать значимых неключевых значений, но в этом случае придется добавить в схему индексной таблицы фиктивную колонку `$empty` типа `int64`, в которой значения всегда будут равны null. +То, какие выборки индекс способен ускорить, определяется схемой индексной таблицы. Для эффективного выбора диапазона чтения необходимо, чтобы вторичный ключ являлся префиксом ключа индексной таблицы. Быстрый доступ до данных в индексируемой таблице достигается за счет хранения полного её ключа (называемого *первичным*) в индексной таблице. Поскольку строки сортированных динамических таблиц должны быть уникальными по ключу, то ключ индексной таблицы должен содержать первичный ключ, то есть представлять собой конкатенацию вторичного и первичного ключей (у этого правила есть [исключение](../../../user-guide/dynamic-tables/secondary-indices#unique)). + +В качестве неключевых колонок индексная таблица может содержать одну или несколько неключевых колонок индексируемой таблицы. Она также может не содержать значимых неключевых значений, но в этом случае придется добавить в схему индексной таблицы фиктивную колонку `$empty` типа `int64`, в которой значения всегда будут равны null. Пример создания таблиц и вторичного индекса, связывающего их: @@ -30,17 +32,17 @@ yt create secondary_index --attributes '{table_path="//path/to/table"; index_tab {% note warning "Примечание" %} -Обратите внимание, что имена и типы колонок в таблицах полностью совпадают. Наличие в индексной таблице колонок, которых нет в индексируемой, или несовпадение их типов привлечет к ошибке создания вторичного индекса (за исключением фиктивной колонки `$empty` - её присутствие в индексируемой таблице не является необходимым). +Обратите внимание, что имена и типы колонок в таблицах полностью совпадают. Наличие в индексной таблице колонок, которых нет в индексируемой, или несовпадение их типов привлечет к ошибке создания вторичного индекса (за исключением фиктивной колонки `$empty` — её присутствие в индексируемой таблице не является необходимым). {% endnote %} -Вторичный индекс может быть создан только если обе таблицы отмонтированы. После создания вторичного индекса и монтирования таблиц *последующие* записи в индексируемую таблицу автоматически отображаются в индексной (о построении индексной таблицы поверх имеющихся данных см. [далее](../../../user-guide/dynamic-tables/secondary-indices#building)). Также становится возможным выполнять эффективные select-запросы с фильтрацией по вторичному ключу используя ключевое слово `WITH INDEX`. Запрос с индексом выполнится эффективно, если предикат позволяет [отсечь](../../../user-guide/dynamic-tables/dyn-query-language#input_data_cut) ненужные строки из индексной таблицы. +Вторичный индекс может быть создан, только если обе таблицы отмонтированы. После создания вторичного индекса и монтирования таблиц *последующие* записи в индексируемую таблицу автоматически отображаются в индексной (о построении индексной таблицы поверх имеющихся данных см. [далее](../../../user-guide/dynamic-tables/secondary-indices#building)). Также становится возможным выполнять эффективные select-запросы с фильтрацией по вторичному ключу, используя ключевое слово `WITH INDEX`. Запрос с индексом выполнится эффективно, если предикат позволяет [отсечь](../../../user-guide/dynamic-tables/dyn-query-language#input_data_cut) ненужные строки из индексной таблицы. ```bash yt select-rows "key, value FROM [//path/to/table] WITH INDEX [//path/to/index/table] where value BETWEEN 0 and 10" ``` -При исполнении запроса с таким синтаксисом система сначала прочитает из индесной таблицы строки, подходящие под предикат `where value BETWEEN 0 and 10`, а затем выполнит внутреннее соединение (inner join) со строками индексируемой таблицы по общим колонкам. То есть такой запрос эквивалетнен следующему: +При исполнении запроса с таким синтаксисом система сначала прочитает из индексной таблицы строки, подходящие под предикат `where value BETWEEN 0 and 10`, а затем выполнит внутреннее соединение (inner join) со строками индексируемой таблицы по общим колонкам. То есть такой запрос эквивалетнен следующему: ```bash yt select-rows "key, value FROM [//path/to/index/table] AS IndexTable JOIN [//path/to/table] ON (IndexTable.key, IndexTable.value) = (key, value) WHERE IndexTable.value BETWEEN 0 and 10" @@ -54,11 +56,11 @@ yt select-rows "key, value FROM [//path/to/index/table] AS IndexTable JOIN [//pa Индексная таблица может содержать отображения не всех строк, а только тех, для которых выполняется некий предикат. Предикат задается атрибутом `predicate` вторичного индекса. Он должен представлять собой валидное булевое выражение над схемой индексируемой таблицы (предикат может использовать колонки, которых нет в индексной таблице). Атрибут `predicate` совместим с любым из видов вторичных индексов. -Это полезно, если запросы, для которых индекс полезен, касаются только некоторой выделяемой части данных. Например, схема таблицы имеет вид `[{name=id; sort_order=ascending; type=uint64}; {name=name; type=string}; {name=age; type=int64}; {name=loyalty; type=boolean}; ...]`, а в запросах вы делаете выборку по возрасту только для строк, в которых `loyalty` имеет не-null значение. В таком случае вторичный индекс с предикатом `not is_null(loyalty)` и индексной таблицей со схемой `[{name=age; type=int64; sort_order=ascending}; {name=id; sort_order=ascending; type=uint64}; {name=$empty, type=int64}]` является оптимальным решением - строки с `loyalty=#` не попадут в индексную таблицу. +Это полезно, если запросы, для которых индекс полезен, касаются только некоторой выделяемой части данных. Например, схема таблицы имеет вид `[{name=id; sort_order=ascending; type=uint64}; {name=name; type=string}; {name=age; type=int64}; {name=loyalty; type=boolean}; ...]`, а в запросах вы делаете выборку по возрасту только для строк, в которых `loyalty` имеет не-null значение. В таком случае вторичный индекс с предикатом `not is_null(loyalty)` и индексной таблицей со схемой `[{name=age; type=int64; sort_order=ascending}; {name=id; sort_order=ascending; type=uint64}; {name=$empty, type=int64}]` является оптимальным решением — строки с `loyalty=#` не попадут в индексную таблицу. ### Индекс над списком { #unfolding } -Индекс вида `kind=unfolding` разворачивает строки по колонке со списком из примитивных типов. Он позволяет делать выборки строк, в которых индексируемый список содержит определенные значения. Например, строке `{id=0; child_ids=[1, 4, 7]}` в индексной таблице будут соответствовать строки `[{child_ids=1; id=0}; {child_ids=4; id=0}; {child_ids=7; id=0}]`. В схемах таблиц должны присутствовать такие одинаково названные колонки, типы которых соотносятся как `list` или `optional>` в индексируемой таблице и `T` в индексной, где `T` - примитивный тип. В select-запросах ограничение на индексируемое значение можно задавать используя функцию `list_contains`. Пример: `id FROM [//path/to/table] WITH INDEX [//path/to/index/table] where list_contains(child_ids, 1)`. +Индекс вида `kind=unfolding` разворачивает строки по колонке со списком из примитивных типов. Он позволяет делать выборки строк, в которых индексируемый список содержит определённые значения. Например, строке `{id=0; child_ids=[1, 4, 7]}` в индексной таблице будут соответствовать строки `[{child_ids=1; id=0}; {child_ids=4; id=0}; {child_ids=7; id=0}]`. В схемах таблиц должны присутствовать такие одинаково названные колонки, типы которых соотносятся как `list` или `optional>` в индексируемой таблице и `T` в индексной, где `T` — примитивный тип. В select-запросах ограничение на индексируемое значение можно задавать используя функцию `list_contains`. Пример: `id FROM [//path/to/table] WITH INDEX [//path/to/index/table] where list_contains(child_ids, 1)`. {% note warning "Примечание" %} @@ -68,15 +70,15 @@ yt select-rows "key, value FROM [//path/to/index/table] AS IndexTable JOIN [//pa {% endnote %} -Например, пусть строки в таблице со схемой `[{name=id; type=int64; sort_order=ascending}; {name=book_name; type=string}; {name=genres; type_v3={type_name=list; item=string}}; ...]` описывают книги. Для быстрой выборки по жанрам можно использовать `unfolding` вторичный индекс над индексной таблицей со схемой `[{name=genres; sort_order=ascending; type=string};{name=id; type=int64; sort_order=ascending}; {name=book_name; type=string}]` - присутствие в схеме индексной таблицы колонки `book_name` позволит использовать индексную таблицу без присоединения к индексируемой для запросов, в которых нужны только колонки `genres`, `book_name` и `id`, ещё уменьшая количество чтений и ускоряя выполнение запросов. +Например, пусть строки в таблице со схемой `[{name=id; type=int64; sort_order=ascending}; {name=book_name; type=string}; {name=genres; type_v3={type_name=list; item=string}}; ...]` описывают книги. Для быстрой выборки по жанрам можно использовать `unfolding` вторичный индекс над индексной таблицей со схемой `[{name=genres; sort_order=ascending; type=string};{name=id; type=int64; sort_order=ascending}; {name=book_name; type=string}]` — присутствие в схеме индексной таблицы колонки `book_name` позволит использовать индексную таблицу без присоединения к индексируемой для запросов, в которых нужны только колонки `genres`, `book_name` и `id`, ещё уменьшая количество чтений и ускоряя выполнение запросов. ### Уникальный индекс { #unique } -Индекс вида `kind=unique` поддерживает уникальность вторичного ключа в индексируемой таблице. Ключом в схеме индексной таблицы для вторичного индекса такого вида является вторичный ключ, а неключевыми значениями - первичный ключ. При попытке записи, которая привела бы к существованию в индексируемой таблице двух строк с одинаковыми значениями вторичного ключа, пользователю вернётся ошибка `UniqueIndexConflict`. Параллельный коммит двух транзакций с записями строк с разными первичными ключами и одинаковыми вторичными приведет к падению одной из транзакций с ошибкой `TransactionLockConflict`. +Индекс вида `kind=unique` поддерживает уникальность вторичного ключа в индексируемой таблице. Ключом в схеме индексной таблицы для вторичного индекса такого вида является вторичный ключ, а неключевыми значениями — первичный ключ. При попытке записи, которая привела бы к существованию в индексируемой таблице двух строк с одинаковыми значениями вторичного ключа, пользователю вернётся ошибка `UniqueIndexConflict`. Параллельный коммит двух транзакций с записями строк с разными первичными ключами и одинаковыми вторичными приведет к падению одной из транзакций с ошибкой `TransactionLockConflict`. ### Шардирование вторичного индекса { #sharding } -Для шардирования данных зачастую используются [вычисляемые ключевые колонки](../../../user-guide/dynamic-tables/resharding#expression), которые для индексной и индексируемой таблицы сделаны независимыми друг от друга - они могут иметь одинаковые или разные имена, типы и выражения, благодаря чему можно независимо контролировать шардирование. Однако обратите внимание, что, также как и в случае обычных таблиц, использование вычисляемой колонки в индексной таблице сделает неэффективными запросы с указанием диапазона по индексируемой колонке. +Для шардирования данных зачастую используются [вычисляемые ключевые колонки](../../../user-guide/dynamic-tables/resharding#expression), которые для индексной и индексируемой таблицы сделаны независимыми друг от друга — они могут иметь одинаковые или разные имена, типы и выражения, благодаря чему можно независимо контролировать шардирование. Однако обратите внимание, что, также как и в случае обычных таблиц, использование вычисляемой колонки в индексной таблице сделает неэффективными запросы с указанием диапазона по индексируемой колонке. ### Копирование и перемещение { #copy-move } @@ -94,7 +96,7 @@ yt select-rows "key, value FROM [//path/to/index/table] AS IndexTable JOIN [//pa * Запись в индексируемую таблицу [map-reduce операцией](../../../user-guide/dynamic-tables/bulk-insert) не отображается в индексной. -* Изменение схемы таблицы со вторичным индексом также имеет особенности. При добавлении ключевой колонки, её нужно сначала добавить в индексные таблицы, а только потом в индексируемую. При добавлении неключевой колонки - наоборот (или её можно не добавлять в индексные таблицы). +* Изменение схемы таблицы со вторичным индексом также имеет особенности. При добавлении ключевой колонки, её нужно сначала добавить в индексные таблицы, а только потом в индексируемую. При добавлении неключевой колонки — наоборот (или её можно не добавлять в индексные таблицы). * Все колонки вторичного ключа, а также колонки, используемые в предикате, должны иметь одинаковую `lock` [группу](../../../user-guide/dynamic-tables/transactions#conflicts) в индексируемой таблице. @@ -104,16 +106,4 @@ yt select-rows "key, value FROM [//path/to/index/table] AS IndexTable JOIN [//pa ## Производительность { #performance } -Общим для всех случаев использования вторичных индексов недостатком является замедление записи в индексируемую таблицу. Запись в индексируемую таблицу повлечет также прозрачное для пользователя чтение из неё и запись во все индексные таблицы, а в случае уникальных индексов - также чтение из индексной таблицы. Эти чтения выполняются в конце транзакции, как следствие - латентность записи увеличивается на латентность чтения. - -## Построение индекса { #building } - -Для корректного создания вторичного индекса поверх существующих данных существует [скрипт](https://a.yandex-team.ru/arcadia/yt/yt/scripts/dynamic_tables/build_secondary_index). Он позволит построить вторичный индекс с минимальным для индексируемой таблицы даунтаймом необходимым на взятие снапшота индексируемой таблицы и её перемонтирование (если только не строится уникальный индекс - для поддержания строгой уникальности необходимо, чтобы в таблицу не происходило записей на протяжении всего построения). После того как скрипт возьмет нужный лок, он самостоятельно примонтирует индексируемую таблицу и её можно будет использовать для чтения и записи. После успешного завершения работы скрипта можно будет использовать индексную таблицу для запросов. Пользователь должен сам проследить за тем, чтобы не задавать запросов с использованием индекса, пока он полностью не построен. - -Скрипт поддерживает реплицированные таблицы, но в этом случае даунтайм еще немного увеличивается - необходимо также подождать, пока реплики не синхронизируются. Его использование предполагает, что индексируемая и индексная таблицы уже существуют. - -{% note warning "Примечание" %} - -После построения вторичного индекса этим скриптом в индексной таблице могут оказаться лишние строки, не соответствующие никакой записи в индексируемой таблице. Это может произойти из-за фонового процесса компактификации и не влияет на корректность при использовании вторичного индекса в select-запросах, так как при соединении таблиц эти строки все равно будут опущены. Построение строго биективного индекса возможно только с полным даунтаймом обеих таблиц, для такого режима построения в скрипте есть флаг. - -{% endnote %} +Недостаток использования вторичных индексов — замедление записи в индексируемую таблицу. Запись в индексируемую таблицу повлечёт также прозрачное для пользователя чтение из неё и запись во все индексные таблицы, а в случае уникальных индексов — также чтение из индексной таблицы. Эти чтения выполняются в конце транзакции, как следствие — латентность записи увеличивается на латентность чтения. diff --git a/yt/docs/ru/toc.yaml b/yt/docs/ru/toc.yaml index 4c86724630b6..c4b2a6515663 100644 --- a/yt/docs/ru/toc.yaml +++ b/yt/docs/ru/toc.yaml @@ -138,6 +138,8 @@ items: href: user-guide/dynamic-tables/bulk-insert.md - name: Язык запросов href: user-guide/dynamic-tables/dyn-query-language.md + - name: Вторичные индексы + href: user-guide/dynamic-tables/secondary-indices.md - name: Примеры items: - name: Создание веб-сервиса