---
metadata:
  - name: generator
    content: Diplodoc Platform v5.50.6
alternate:
  - https://ytsaurus.tech/docs/en/user-guide/dynamic-tables/ordered-dynamic-tables.md
  - https://ytsaurus.tech/docs/ru/user-guide/dynamic-tables/ordered-dynamic-tables.md
---
> **Documentation Index:** Fetch the complete configuration index at https://ytsaurus.tech/docs/ru/llms.txt

<!-- source: ru/_includes/user-guide/dynamic-tables/ordered-dynamic-tables.md -->
# Упорядоченные динамические таблицы

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

## Модель данных { #model }

Упорядоченные динамические таблицы — это разновидность таблиц в YTsaurus, представляющая собой простую упорядоченную последовательность строк. Каждая строка упорядоченной таблицы состоит из колонок и подчинена схеме таблицы, указываемой при создании. Ключевые колонки в таких таблицах отсутствуют.

Упорядоченные динамические таблицы поддерживают добавление новых строк в конец в рамках транзакции, а также чтение строк по их индексам без транзакционной изоляции.

Ближайшим аналогом упорядоченных динамических таблиц является [Apache Kafka](https://kafka.apache.org).

Как и в случае [сортированных динамических таблиц](https://ytsaurus.tech/docs/ru/user-guide/dynamic-tables/sorted-dynamic-tables.md), пространство ключей упорядоченной динамической таблицы делится на [таблеты](https://ytsaurus.tech/docs/ru/user-guide/dynamic-tables/overview.md#tablets).

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

 Упорядоченная динамическая таблица исходно состоит из одного таблета. Множество таблетов можно менять с помощью команды `reshard_table`. Она позволяет изменить структуру для набора таблетов, идущих подряд. В упорядоченных динамических таблицах при [перешардировании](#reshard) указывается новое количество таблетов, на которые следует заменить исходные. Указание `pivot_keys` не требуется. Существующие данные перераспределяются между новыми таблетами неспецифицированным образом.

## Поддерживаемые операции { #methods }

### Создание

Для создания динамической упорядоченной таблицы необходимо выполнить команду `create table`, указав в атрибутах схему и настройку `dynamic=True`. Схема должна соответствовать схеме упорядоченной таблицы, в частности, среди её колонок не должно быть ключевых.

```bash
yt create table //path/to/table --attributes \
'{dynamic=%true;schema=[{name=first_name;type=string};{name=last_name;type=string}]}'
```

При создании таблицы можно указать начальный индекс строки в каждом таблете. Это имеет смысл, например, при пересоздании очереди, если потребители полагаются на значения `row_index`. Для этого используется атрибут `trimmed_row_counts`. Если он передаётся, необходимо также указывать атрибут `tablet_count`, который должен быть равен длине списка `trimmed_row_counts`.

```bash
yt create table //path/to/table --attributes \
'{dynamic=%true;schema=[{name=first_name;type=string};{name=last_name;type=string}]}; \
tablet_count=5; \
trimmed_row_counts=[10;20;30;40;50]}'
```

### Запись строк

Для записи в упорядоченную динамическую таблицу служит команда `insert_rows`. По умолчанию данные записываются в случайный замонтированный таблет. Для того чтобы управлять распределением данных в ручном режиме, можно в записываемых строках использовать специальную системную колонку `$tablet_index`, имеющую тип `int64`. Значения в этой колонке должны представлять собой числа от 0 до N - 1, где N — число таблетов в таблице. Соответствующие строки будут записаны строго в указанный таблет. Строки, для которых такая аннотация отсутствует, будут записаны в случайный примонтированный таблет.

Запись, произведённая в рамках транзакции, транзакционна: если транзакция завершается успешно, то строки появляются в соответствующих таблицах, если неудачно, то не появляются. При этом в рамках одной транзакции можно работать как с сортированными, так и с упорядоченными таблицами.

### Чтение строк

Читать данные из упорядоченных таблиц можно с помощью [SQL-подобного языка запросов](https://ytsaurus.tech/docs/ru/user-guide/dynamic-tables/dyn-query-language.md) и команды `select_rows`. При этом каждая упорядоченная динамическая таблица предстаёт как сортированная с системными ключевыми колонками `($tablet_index, $row_index)` (оба типа `int64`), а также всеми указанными в схеме таблицы колонками данных.

Например, так выглядит запрос, читающий диапазон строк из фиксированного таблета упорядоченной таблицы:

```sql
* from [//path/to/table] where [$tablet_index] = 10 and [$row_index] between 100 and 200
```

### Изменение схемы и типа таблицы

Изменить схему уже существующей динамической таблицы можно с помощью команды `alter-table`. Для успешного выполнения команды таблица должна быть [отмонтирована](https://ytsaurus.tech/docs/ru/user-guide/dynamic-tables/overview.md#mount_table), а новая схема должна быть совместима со старой. При этом никаких изменений в записанных на диск данных не происходит, поскольку старые данные удовлетворяют новой схеме.

С помощью `alter-table` можно превратить упорядоченную динамическую таблицу в статическую и обратно. Подробности можно узнать в разделе [MapReduce по динамическим таблицам](https://ytsaurus.tech/docs/ru/user-guide/dynamic-tables/mapreduce.md#convert_table).

### Trim

{% note warning "Внимание" %}

Использование reshard вместе с trim запрещено, так как может приводить к непредвиденным последствиям.

{% endnote %}

В общем случае из упорядоченной динамической таблицы нельзя удалить данные. Но существует исключение: в каждом таблете можно удалить начальный отрезок строк. Для этого необходимо использовать команду `trim_rows`. В качестве аргументов ей передаётся путь к таблице, номер таблета, а также аргумент `trimmed_row_count`, показывающий сколько строк в таблице будет удалено после выполнения данной команды. При таком удалении нумерация строк сохраняется. Например, при первом вызове для `trimmed_row_count = 10` будут удалены строки с номерами с 0 по 9 включительно. Затем при вызове с `trimmed_row_count = 30` — строки с 10 по 29 включительно и т. д. `trimmed_row_count` имеет не относительный смысл, а абсолютный и указывает не количество строк, которые будут дополнительно удалены при очередном вызове, а какие именно начальные строки будут удалены после вызова.

Команда `trim_rows` выполняется вне транзакций. После того как она отработала, удалённые данные уже нельзя прочитать командой `select_rows`. Как только оказывается, что в таблете удалено столько строк, что они образуют целый начальный чанк, узел кластера, обслуживающий таблет, посылает сигнал мастер-серверу, и данный чанк удаляется целиком. Именно в этот момент освобождается дисковое пространство.

Количество удалённых строк в любом таблете можно узнать из атрибута `trimmed_row_count` таблета. Данный параметр обновляется асинхронно, то есть между выполнением `trim_rows` и его изменением может пройти некоторое время.

При отмонтировании и последующем монтировании таблета число начальных удалённых строк сохраняется, что гарантирует неизменность нумерации.

{% note warning %}

При превращении динамической таблицы в статическую с помощью команды `alter_table` информация о том, какие строки были удалены, теряется. Также теряется и порядок следования строк в таблице. Порядок строк внутри чанков остаётся неизменным, но на это сложно опираться. Действительно, у динамической таблицы удалёнными могут быть помечены некоторые начальные строки некоторых чанков, и эта информация не может быть сохранена при превращении таблицы в статическую. В итоге получается, что при конвертации динамической таблицы в статическую возможно появление в таблице некоторых из ранее удалённых строк.

{% endnote %}

### Решардирование { #reshard }

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

При этом система старается сохранить инвариант «то, что удалено, более не становится доступно». Поскольку при решардировании система добавляет чанки из удаляемых таблетов в конец последнего сохраняющегося, то при наличии удалённых строк в удаляемых таблетах решардирование затруднено. Для успешного завершения решардирования требуется, чтобы в удаляемых таблетах удалённые строки образовывали префикс по чанкам.

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

Если при решардировании число таблетов увеличивается, в каждом создаваемом таблете можно указать начальный индекс строки. Для этого нужно передать аргументом команды список `trimmed_row_counts`. Его длина должна быть равна числу создаваемых таблетов, то есть `tablet_count - (last_tablet_index - first_tablet_index + 1)`.

```bash
yt reshard-table //path/to/table --tablet-count 5 --first-tablet-index 1 --last-tablet-index 3 --trimmed-row-counts 10 20
# Resulting offsets, assuming table had 5 tablets:
# (old tablet #0) (old tablet #1) (old tablet #2) (old tablet #3) 10 20 (old tablet #4)
```

### Автоматическое удаление старых строк (TTL) { #remove_old_data }

К данным упорядоченных таблиц применяются те же настройки удаления старых данных, что и у сортированных таблиц. Подробнее можно прочитать в разделе [Удаление старых данных](https://ytsaurus.tech/docs/ru/user-guide/dynamic-tables/sorted-dynamic-tables.md#remove_old_data). Существенное отличие состоит в том, что в упорядоченных таблицах у строки всегда есть только одна версия. Тогда настройки по очистке можно интерпретировать следующим образом:

- если `min_data_versions > 0` (по умолчанию значение 1), то автоматического удаления не происходит;
- система YTsaurus не удаляет строки, записанные менее чем за `min_data_ttl` до текущего момента;
- если `max_data_versions = 0`, можно удалять строки, записанные позднее `min_data_ttl`;
- если `max_data_versions > 0`, можно удалять строки, записанные позднее `max_data_ttl`.

{% note info "Примечание" %}

Автоматическое удаление (trim) применяется сразу ко всему чанку. Пока в чанке есть строки, которые нельзя удалить, все строки из чанка будут доступны.

{% endnote %}

### Ограничение на размер данных { #max_data_weight }

Для ограничения размера данных в упорядоченной таблице можно выставить максимальный допустимый размер таблета. Для этого достаточно указать его в байтах в `@mount_config/max_ordered_tablet_data_weight` на таблице.

## Колонка $timestamp

В схеме упорядоченной динамической таблицы можно указать специальную системную колонку `$timestamp` типа `uint64`. Значение в данной колонке формируется системой автоматически при записи, оно равно commit timestamp для транзакции, в которой строки оказались добавлены в таблицу.

## Колонка $cumulative_data_weight { #cumulative_data_weight_column }

В схеме упорядоченной динамической таблицы можно указать специальную системную колонку `$cumulative_data_weight` типа `int64`. Значение в данной колонке формируется автоматически при записи. Оно равно суммарному логическому весу строк в байтах в таблете, считая от начальной строки с индексом ноль до текущей, включительно. Вес самой колонки `$cumulative_data_weight` также учитывается в этом значении.

При добавлении этой колонки в схему уже существующей таблицы (через отмонтирование и запрос `alter_table`), начальное значение `$cumulative_data_weight` берётся из метаданных чанков таблицы.

## Видимость изменений, strong/weak commit ordering { #commit_ordering }

Уровень консистентности упорядоченных динамических таблиц принципиально ниже такового для сортированных: закоммиченные данные в общем случае могут быть видны не сразу после коммита, а также могут добавиться не в том порядке, в котором коммиты происходили.

При использовании распределённого коммита — когда его участниками оказываются несколько таблет-селлов — оказывается, что непосредственное появление данных в таблетах различных таблет-селлов происходит в различные моменты времени. Действительно, вторая фаза протокола (коммит) выполняется физически распределёнными участниками. Более того: один и тот же таблет-селл может быть участником нескольких распределённых транзакций, причём значения их `commit-ts` и фактическая последовательность, в которой данный таблет-селл выполняет коммит, в общем случае не подчинены никаким условиям. Участник может выполнить коммит транзакции `A` до коммита транзакции `B` даже когда `commit-ts(A) > commit-ts(B)`.

В случае динамических сортированных таблиц указанная деталь реализации скрыта от пользователя snapshot-изоляцией: несмотря на то что данные из транзакции `A` уже добавлены в таблицу, прочитать их можно, лишь заказав `ts`, не меньший `commit-ts(A)`. Кроме того, система блокировок строк гарантирует, что подобная инверсия не будет наблюдаться на одном и том же ключе в случае, когда интервалы `[start-ts,commit-ts]` для транзакций пересекаются.

Для динамических упорядоченных таблиц ситуация иная: обычно они не хранят commit timestamps, исключая случай поля `$timestamp`, и не поддерживают snapshot-изоляцию. Поэтому порядок, в котором записи будут добавляться в конец упорядоченной динамической таблицы, не имеет ничего общего с порядком на commit timestamps.

В случае, когда упорядоченная динамическая таблица участвует в двухфазном коммите, подтверждение успешного коммита от координатора не значит, что все участники также произвели коммит и данные действительно добавлены в конец таблицы (двухфазный коммит — это коммит, который задевает более одного таблет-селла; таковы, например, все коммиты, в которых упорядоченная динамическая таблица является синхронной репликой). Попытка найти закоммиченные строки на участнике сразу после успешного окончания двухфазного коммита может закончиться неудачей: эти строки станут видны лишь через некоторое время.

Можно получить определённые гарантии монотонности для добавленных строк. У таблицы есть системный атрибут `commit_ordering`, который управляет порядком занесения строк в таблицу:

- `weak` — режим по умолчанию, строки попадают в упорядоченную таблицу сразу в момент коммита участника, который запаздывает относительно координатора, порядок относительно commit timestamps не гарантируется;
- `strong` — гарантируется, что строки попадают в таблицу в порядке commit timestamps.

В режиме `strong` при наличии поля `$timestamp` таблица оказывается упорядоченной по этому полю. Обратите внимание — это не делает данное поле ключевым.

Режим `strong` реализован следующим образом: каждый таблет-селл отслеживает специальный `barrier-ts`, который постоянно монотонно возрастает и про который известно, что ни одна транзакция не может получить `commit-ts` меньше `barrier-ts`. Строки, записанные в рамках транзакции в упорядоченную динамическую таблицу с режимом `strong`, попадают в неё не сразу в момент коммита, когда `barrier-ts` становится больше `commit-ts` данной транзакции. Тем самым система сериализует все транзакции по `commit-ts`, но только те, для которых `commit-ts < barrier-ts`. Для транзакций с `commit-ts > barrier-ts` система может определить относительный порядок, но не может гарантировать, что в будущем не появится новой транзакции, которая нарушит установленный порядок.

Для статических таблиц атрибут `commit_ordering` также присутствует, но он всегда равен `weak`.
<!-- endsource: ru/_includes/user-guide/dynamic-tables/ordered-dynamic-tables.md -->