Перейти к содержимому
Последнее обновление

Обновления из нескольких потоков для модели Unique

Сценарии использования и основные проблемы​

В хранилищах данных с широкими таблицами разные столбцы одной таблицы часто поступают из разных источников данных или разных конвейеров обработки. Типичные примеры:

  • Поток реального времени: непрерывно обновляет часть полей таблицы (например, столбцы, связанные с поведением пользователей).
  • Офлайн-поток (или поток по требованию): обновляет другие поля таблицы в определённые моменты времени (например, столбцы, связанные с профилями пользователей или тегами).

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

ПроблемаОписание
Перезапись при одновременном обновленииРазные потоки данных поступают в разное время. Простая агрегация REPLACE не гарантирует, что данные каждого потока будут обновляться в порядке версий.
Высокие затраты на объединение данныхПрактически невозможно дождаться, пока данные всех столбцов будут собраны перед записью. Ожидание может оказаться слишком долгим и неприемлемым для бизнеса.

Модель Unique в Doris изначально поддерживает контроль версий на основе столбца Sequence, но позволяет определить только один глобальный столбец Sequence. Это не даёт возможности независимо управлять версиями обновлений разных столбцов из разных потоков данных. Для решения этой задачи в Doris предусмотрена функция Sequence Mapping.

Решение: Sequence Mapping​

Sequence Mapping — это механизм одновременного обновления из нескольких потоков, поддерживаемый моделью Unique в Doris. Он позволяет назначить разные столбцы Sequence разным столбцам данных и тем самым обеспечить независимый контроль версий для каждого потока данных без взаимного влияния.

Принцип работы​

Предположим, таблица модели Unique содержит следующие столбцы:

СтолбецABCDEs1s2
РольKeyKeyValueValueValueSequenceSequence
  • Поток данных 1 записывает данные в столбцы A, B, C, D и использует s1 в качестве столбца контроля версий.
  • Поток данных 2 записывает данные в столбцы A, B, E и использует s2 в качестве столбца контроля версий.

При таком сопоставлении:

  • s1 управляет версией столбцов C, D: только если s1 новых данных больше, чем s1 существующих данных, обновляются C, D, s1.
  • s2 управляет версией столбца E: только если s2 новых данных больше, чем s2 существующих данных, обновляются E, s2.
  • Эти два потока не влияют друг на друга, а запросы могут читать актуальные данные всех столбцов.

Пример использования​

1. Создание таблицы с поддержкой Sequence Mapping​

Используйте свойство sequence_mapping.<sequence_col> в разделе PROPERTIES, чтобы задать сопоставление между столбцом Sequence и столбцами Value, которыми он управляет.

В следующем примере создаётся таблица модели Unique, в которой версиями столбцов c, d управляет s1, а версиями столбца e управляет s2:

CREATE TABLE `upsert_test` (
`a` bigint(20) NULL COMMENT "",
`b` int(11) NULL COMMENT "",
`c` int(11) NULL COMMENT "",
`d` int(11) NULL COMMENT "",
`e` int(11) NULL COMMENT "",
`s1` int(11) NULL COMMENT "",
`s2` int(11) NULL COMMENT ""
) ENGINE=OLAP
UNIQUE KEY(`a`, `b`)
COMMENT "OLAP"
DISTRIBUTED BY HASH(`a`, `b`) BUCKETS 1
PROPERTIES (
"enable_unique_key_merge_on_write" = "false",
"light_schema_change" = "true",
"replication_num" = "1",
"sequence_mapping.s1" = "c,d",
"sequence_mapping.s2" = "e"
);

Поддерживаемые типы столбцов Sequence: целочисленные типы, DATE и DATETIME. После создания столбца его тип изменить нельзя.

После создания схема таблицы выглядит следующим образом:

MySQL > desc upsert_test;
+-------+--------+------+-------+---------+---------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------+------+-------+---------+---------+
| a | bigint | Yes | true | NULL | |
| b | int | Yes | true | NULL | |
| c | int | Yes | false | NULL | REPLACE |
| d | int | Yes | false | NULL | REPLACE |
| e | int | Yes | false | NULL | REPLACE |
| s1 | int | Yes | false | NULL | REPLACE |
| s2 | int | Yes | false | NULL | REPLACE |
+-------+--------+------+-------+---------+---------+

2. Запись данных и выполнение запросов​

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

Вставка 1: записываются только c, d, s1, а e, s2 не записываются.

MySQL > insert into upsert_test(a, b, c, d, s1) values (1,1,2,2,2);
Query OK, 1 row affected (0.080 sec)

MySQL > select * from upsert_test;
+------+------+------+------+------+------+------+
| a | b | c | d | e | s1 | s2 |
+------+------+------+------+------+------+------+
| 1 | 1 | 2 | 2 | NULL | 2 | NULL |
+------+------+------+------+------+------+------+

Вставка 2: s1=1 меньше существующего значения s1=2, поэтому c, d, s1 не обновляются.

MySQL > insert into upsert_test(a, b, c, d, s1) values (1,1,1,1,1);
Query OK, 1 row affected (0.048 sec)

MySQL > select * from upsert_test;
+------+------+------+------+------+------+------+
| a | b | c | d | e | s1 | s2 |
+------+------+------+------+------+------+------+
| 1 | 1 | 2 | 2 | NULL | 2 | NULL |
+------+------+------+------+------+------+------+

Вставка 3: записываются e, s2. Поскольку предыдущее значение s2 было NULL (считается наименьшим значением), e, s2 обновляются.

MySQL > insert into upsert_test(a, b, e, s2) values (1,1,2,2);
Query OK, 1 row affected (0.043 sec)

MySQL > select * from upsert_test;
+------+------+------+------+------+------+------+
| a | b | c | d | e | s1 | s2 |
+------+------+------+------+------+------+------+
| 1 | 1 | 2 | 2 | 2 | 2 | 2 |
+------+------+------+------+------+------+------+

Вставка 4: s1=3 больше существующего значения s1=2, поэтому c, d, s1 обновляются, а e, s2 остаются без изменений.

MySQL > insert into upsert_test(a, b, c, d, s1) values (1,1,3,3,3);
Query OK, 1 row affected (0.049 sec)

MySQL > select * from upsert_test;
+------+------+------+------+------+------+------+
| a | b | c | d | e | s1 | s2 |
+------+------+------+------+------+------+------+
| 1 | 1 | 3 | 3 | 2 | 3 | 2 |
+------+------+------+------+------+------+------+

Вставка 5: записываются обе группы столбцов. И s1=4, и s2=4 больше существующих значений, поэтому обновляются все столбцы.

MySQL > insert into upsert_test(a, b, c, d, s1, e, s2) values (1,1,5,5,4,5,4);
Query OK, 1 row affected (0.050 sec)

MySQL > select * from upsert_test;
+------+------+------+------+------+------+------+
| a | b | c | d | e | s1 | s2 |
+------+------+------+------+------+------+------+
| 1 | 1 | 5 | 5 | 5 | 4 | 4 |
+------+------+------+------+------+------+------+

Сводка результатов обновления:

ОперацияУсловие срабатыванияОбновлённые столбцы
Вставка 1Первая запись c, d, s1c, d, s1
Вставка 2Новое значение s1 < существующее значение s1Нет
Вставка 3Первая запись e, s2e, s2
Вставка 4Новое значение s1 > существующее значение s1c, d, s1
Вставка 5Оба новых значения s1 и s2 больше существующих значенийc, d, s1, e, s2

3. Добавление и удаление столбцов​

Таблица с Sequence Mapping поддерживает динамическое добавление и удаление столбцов и сопоставлений с помощью ALTER TABLE.

Создание исходной таблицы: таблица содержит только одно сопоставление s1 -> c,d.

CREATE TABLE `upsert_test` (
`a` bigint(20) NULL COMMENT "",
`b` int(11) NULL COMMENT "",
`c` int(11) NULL COMMENT "",
`d` int(11) NULL COMMENT "",
`s1` int(11) NULL COMMENT ""
) ENGINE=OLAP
UNIQUE KEY(`a`, `b`)
COMMENT "OLAP"
DISTRIBUTED BY HASH(`a`, `b`) BUCKETS 1
PROPERTIES (
"enable_unique_key_merge_on_write" = "false",
"light_schema_change" = "true",
"replication_num" = "1",
"sequence_mapping.s1" = "c,d"
);

Запись исходных данных:

MySQL > insert into upsert_test(a, b, c, d, s1) values (1,1,1,1,1),(1,1,3,3,3),(1,1,2,2,2);
Query OK, 3 rows affected (0.101 sec)

MySQL > select * from upsert_test;
+------+------+------+------+------+
| a | b | c | d | s1 |
+------+------+------+------+------+
| 1 | 1 | 3 | 3 | 3 |
+------+------+------+------+------+

Добавление столбцов и сопоставления: используйте ALTER TABLE, чтобы добавить столбцы e, s2 и одновременно задать сопоставление s2 -> e.

MySQL > alter table upsert_test add column (e int(11) NULL, s2 bigint) PROPERTIES('sequence_mapping.s2' = 'e');
Query OK, 0 rows affected (0.011 sec)

MySQL > desc upsert_test;
+-------+--------+------+-------+---------+---------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------+------+-------+---------+---------+
| a | bigint | Yes | true | NULL | |
| b | int | Yes | true | NULL | |
| c | int | Yes | false | NULL | REPLACE |
| d | int | Yes | false | NULL | REPLACE |
| s1 | int | Yes | false | NULL | REPLACE |
| e | int | Yes | false | NULL | REPLACE |
| s2 | bigint | Yes | false | NULL | REPLACE |
+-------+--------+------+-------+---------+---------+

MySQL > select * from upsert_test;
+------+------+------+------+------+------+------+
| a | b | c | d | s1 | e | s2 |
+------+------+------+------+------+------+------+
| 1 | 1 | 3 | 3 | 3 | NULL | NULL |
+------+------+------+------+------+------+------+

Запись после вступления нового сопоставления в силу:

MySQL > insert into upsert_test(a, b, e, s2) values (1,1,2,2);
Query OK, 1 row affected (0.052 sec)

MySQL > select * from upsert_test;
+------+------+------+------+------+------+------+
| a | b | c | d | s1 | e | s2 |
+------+------+------+------+------+------+------+
| 1 | 1 | 3 | 3 | 3 | 2 | 2 |
+------+------+------+------+------+------+------+

MySQL > insert into upsert_test(a, b, c, d, s1, e, s2) values (1,1,5,5,4,5,4);
Query OK, 1 row affected (0.050 sec)

MySQL > select * from upsert_test;
+------+------+------+------+------+------+------+
| a | b | c | d | s1 | e | s2 |
+------+------+------+------+------+------+------+
| 1 | 1 | 5 | 5 | 4 | 5 | 4 |
+------+------+------+------+------+------+------+

Удаление столбцов: удалите столбец Value e, после чего соответствующее сопоставление перестанет действовать. Затем удалите столбец Sequence s2.

MySQL > alter table upsert_test drop column e;
Query OK, 0 rows affected (0.006 sec)

MySQL > select * from upsert_test;
+------+------+------+------+------+------+
| a | b | c | d | s1 | s2 |
+------+------+------+------+------+------+
| 1 | 1 | 5 | 5 | 4 | 4 |
+------+------+------+------+------+------+

MySQL > alter table upsert_test drop column s2;
Query OK, 0 rows affected (0.005 sec)

MySQL > select * from upsert_test;
+------+------+------+------+------+
| a | b | c | d | s1 |
+------+------+------+------+------+
| 1 | 1 | 5 | 5 | 4 |
+------+------+------+------+------+

Ограничения использования​

При использовании Sequence Mapping учитывайте следующие ограничения:

КатегорияОграничение
Настройки при создании таблицыПараметр light_schema_change должен быть включён. Если свойство sequence_mapping не задано при создании таблицы, включить его позднее нельзя.
Типы столбцовСтолбцы Sequence поддерживают только целочисленные типы и типы даты и времени (DATE, DATETIME); после создания столбца его тип изменить нельзя.
Роли столбцовНи столбцы Sequence, ни сопоставленные им столбцы не могут быть столбцами Key. Все столбцы, кроме Key, должны быть сопоставлены со столбцом Sequence.
Связи сопоставленияНаборы столбцов, сопоставленных разным столбцам Sequence, не могут пересекаться (например, d нельзя сопоставить одновременно с s1 и s2). После создания сопоставления его нельзя изменить (например, столбец, уже сопоставленный с s1, нельзя переназначить столбцу s2).
Ограничения DDLПереименование столбцов не поддерживается. Создание Rollup не поддерживается.
Режим храненияВ настоящее время поддерживаются только таблицы MOR (Merge-on-Read). Одновременное использование с глобальным столбцом Sequence не поддерживается. Пакетные операции удаления не поддерживаются.
Семантика загрузкиПоля, не указанные при загрузке, автоматически заполняются значениями по умолчанию или NULL. При сравнении столбцов Sequence значение NULL считается наименьшим значением.