paint-brush
Эволюция сегментирования данных в сторону автоматизации и гибкости в Apache Dorisк@shirleyfromapachedoris
461 чтения
461 чтения

Эволюция сегментирования данных в сторону автоматизации и гибкости в Apache Doris

к Shirley H.29m2024/07/20
Read on Terminal Reader

Слишком долго; Читать

Apache Doris представляет функцию автоматического разделения в версии 2.1.0. Начиная с Apache Doris 2.1.0, он упростил управление DDL и разделами. Он полезен при крупномасштабной обработке данных и позволяет пользователям легко перейти с других систем баз данных на Apache Doris.
featured image - Эволюция сегментирования данных в сторону автоматизации и гибкости в Apache Doris
Shirley H. HackerNoon profile picture


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


Как и во многих базах данных, Apache Doris разбивает данные на разделы, а затем раздел делится на сегменты. Разделы обычно определяются временем или другими непрерывными значениями. Это позволяет механизмам запросов быстро находить целевые данные во время запросов, отсекая ненужные диапазоны данных.


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


До версии 2.1.0 в Apache Doris можно было создавать разделы данных двумя способами:


  • Разделение вручную : пользователи указывают разделы в операторе создания таблицы или впоследствии изменяют их с помощью операторов DDL.


  • Динамический раздел : система автоматически поддерживает разделы в заранее определенном диапазоне в зависимости от времени приема данных.


В Apache Doris 2.1.0 мы представили Auto Partition . Он поддерживает секционирование данных по RANGE или LIST и дополнительно повышает гибкость помимо автоматического секционирования.

Эволюция стратегий разделения в Doris

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


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


Раздел вручную

Doris позволяет пользователям вручную создавать разделы данных по ДИАПАЗОНУ и СПИСКУ.


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


 CREATE TABLE IF NOT EXISTS example_range_tbl ( `user_id` LARGEINT NOT NULL COMMENT "User ID", `date` DATE NOT NULL COMMENT "Data import date", `timestamp` DATETIME NOT NULL COMMENT "Data import timestamp", `city` VARCHAR(20) COMMENT "Location of user", `age` SMALLINT COMMENT "Age of user", `sex` TINYINT COMMENT "Sex of user", `last_visit_date` DATETIME REPLACE DEFAULT "1970-01-01 00:00:00" COMMENT "Last visit date of user", `cost` BIGINT SUM DEFAULT "0" COMMENT "User consumption", `max_dwell_time` INT MAX DEFAULT "0" COMMENT "Maximum dwell time of user", `min_dwell_time` INT MIN DEFAULT "99999" COMMENT "Minimum dwell time of user" ) ENGINE=OLAP AGGREGATE KEY(`user_id`, `date`, `timestamp`, `city`, `age`, `sex`) PARTITION BY RANGE(`date`) ( PARTITION `p201701` VALUES LESS THAN ("2017-02-01"), PARTITION `p201702` VALUES LESS THAN ("2017-03-01"), PARTITION `p201703` VALUES LESS THAN ("2017-04-01"), PARTITION `p2018` VALUES [("2018-01-01"), ("2019-01-01")) ) DISTRIBUTED BY HASH(`user_id`) BUCKETS 16 PROPERTIES ( "replication_num" = "1" );


Таблица разбита на разделы date импорта данных и предварительно создано 4 раздела. Внутри каждого раздела данные дополнительно делятся на 16 сегментов на основе хеш-значения user_id .


Благодаря такому дизайну секционирования и группирования при запросе данных, начиная с 2018 года, системе необходимо сканировать только раздел p2018 . Вот как выглядит запрос SQL:


 mysql> desc select count() from example_range_tbl where date >= '20180101'; +--------------------------------------------------------------------------------------+ | Explain String(Nereids Planner) | +--------------------------------------------------------------------------------------+ | PLAN FRAGMENT 0 | | OUTPUT EXPRS: | | count(*)[#11] | | PARTITION: UNPARTITIONED | | | | ...... | | | | 0:VOlapScanNode(193) | | TABLE: test.example_range_tbl(example_range_tbl), PREAGGREGATION: OFF. | | PREDICATES: (date[#1] >= '2018-01-01') | | partitions=1/4 (p2018), tablets=16/16, tabletList=561490,561492,561494 ... | | cardinality=0, avgRowSize=0.0, numNodes=1 | | pushAggOp=NONE | | | +--------------------------------------------------------------------------------------+


Если данные распределены по разделам неравномерно, механизм сегментирования на основе хэша может дополнительно разделить данные на основе user_id . Это помогает избежать дисбаланса нагрузки на некоторых машинах во время запросов и хранения.


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


 CREATE TABLE `DAILY_TRADE_VALUE` ( `TRADE_DATE` datev2 NOT NULL COMMENT 'Trade date', `TRADE_ID` varchar(40) NOT NULL COMMENT 'Trade ID', ...... ) UNIQUE KEY(`TRADE_DATE`, `TRADE_ID`) PARTITION BY RANGE(`TRADE_DATE`) ( PARTITION p_200001 VALUES [('2000-01-01'), ('2000-02-01')), PARTITION p_200002 VALUES [('2000-02-01'), ('2000-03-01')), PARTITION p_200003 VALUES [('2000-03-01'), ('2000-04-01')), PARTITION p_200004 VALUES [('2000-04-01'), ('2000-05-01')), PARTITION p_200005 VALUES [('2000-05-01'), ('2000-06-01')), PARTITION p_200006 VALUES [('2000-06-01'), ('2000-07-01')), PARTITION p_200007 VALUES [('2000-07-01'), ('2000-08-01')), PARTITION p_200008 VALUES [('2000-08-01'), ('2000-09-01')), PARTITION p_200009 VALUES [('2000-09-01'), ('2000-10-01')), PARTITION p_200010 VALUES [('2000-10-01'), ('2000-11-01')), PARTITION p_200011 VALUES [('2000-11-01'), ('2000-12-01')), PARTITION p_200012 VALUES [('2000-12-01'), ('2001-01-01')), PARTITION p_200101 VALUES [('2001-01-01'), ('2001-02-01')), ...... ) DISTRIBUTED BY HASH(`TRADE_DATE`) BUCKETS 10 PROPERTIES ( ...... );


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

Динамический раздел

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


Это пример инструкции CREATE TABLE для таблицы, секционированной по дням. Параметры start и end установлены на -7 и 3 соответственно, что означает, что разделы данных на следующие 3 дня будут созданы заранее, а исторические разделы старше 7 дней будут восстановлены.


 CREATE TABLE `DAILY_TRADE_VALUE` ( `TRADE_DATE` datev2 NOT NULL COMMENT 'Trade date', `TRADE_ID` varchar(40) NOT NULL COMMENT 'Trade ID', ...... ) UNIQUE KEY(`TRADE_DATE`, `TRADE_ID`) PARTITION BY RANGE(`TRADE_DATE`) () DISTRIBUTED BY HASH(`TRADE_DATE`) BUCKETS 10 PROPERTIES ( "dynamic_partition.enable" = "true", "dynamic_partition.time_unit" = "DAY", "dynamic_partition.start" = "-7", "dynamic_partition.end" = "3", "dynamic_partition.prefix" = "p", "dynamic_partition.buckets" = "10" );


Со временем таблица всегда будет поддерживать разделы в диапазоне [current date - 7, current date + 3] . Динамическое разделение особенно полезно для сценариев приема данных в реальном времени, например, когда уровень ODS (хранилище операционных данных) напрямую получает данные из внешних источников, таких как Kafka.


Параметры start и end определяют фиксированный диапазон для разделов, позволяя пользователю управлять разделами только в пределах этого диапазона. Однако если пользователю необходимо включить больше исторических данных, ему придется набрать start значение, а это может привести к ненужным издержкам метаданных в кластере.


Поэтому при применении Dynamic Partition приходится искать компромисс между удобством и эффективностью управления метаданными.

Слова разработчиков

По мере усложнения бизнеса Dynamic Partition становится неадекватным, потому что:


  • Он поддерживает разделение только по RANGE, но не по LIST.
  • Его можно применить только к текущим реальным временным меткам.
  • Он поддерживает только один непрерывный диапазон разделов и не может размещать разделы за пределами этого диапазона.


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


Мы выяснили, что идеальная реализация секционирования должна:


  • Избавьте от необходимости создавать разделы вручную после создания таблицы;
  • Иметь возможность разместить все принятые данные в соответствующих разделах.


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


Затем мы начали думать о следующем: что, если мы отложим создание разделов до тех пор, пока данные не будут приняты, вместо того, чтобы делать это во время создания таблицы или посредством обычного опроса? Вместо предварительного построения распределения разделов мы можем определить правила сопоставления «данные в разделы», чтобы разделы создавались после поступления данных.


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

Автоматический раздел

С помощью Apache Doris 2.1.0 мы реализуем вышеописанный план. Во время приема данных Дорис создает разделы данных на основе настроенных правил. Узлы Doris Backend, отвечающие за обработку и распространение данных, попытаются найти подходящий раздел для каждой строки данных в операторе DataSink плана выполнения. Он больше не фильтрует данные, которые не помещаются ни в один существующий раздел, и не сообщает об ошибке в такой ситуации, а автоматически создает разделы для всех принятых данных.

Автоматическое разделение по ДИАПАЗОНУ

Auto Partition от RANGE предоставляет оптимизированное решение для разделения на основе временного измерения. Он более гибок, чем Dynamic Partition, с точки зрения настройки параметров. Синтаксис для него следующий:


 AUTO PARTITION BY RANGE (FUNC_CALL_EXPR) () FUNC_CALL_EXPR ::= DATE_TRUNC ( <partition_column>, '<interval>' )


Указанный выше <partition_column> — это столбец раздела (т. е. столбец, на котором основано разделение). <interval> указывает единицу раздела, которая соответствует желаемой ширине каждого раздела.


Например, если столбец разделения имеет значение k0 и вы хотите выполнить разделение по месяцам, оператор разделения будет иметь вид AUTO PARTITION BY RANGE (DATE_TRUNC(k0, 'month')) . Для всех импортированных данных система вызовет DATE_TRUNC(k0, 'month') чтобы вычислить левую конечную точку раздела, а затем правую конечную точку, добавив один interval .


Теперь мы можем применить автоматическое разделение к таблице DAILY_TRADE_VALUE представленной в предыдущем разделе о динамическом разделе.


 CREATE TABLE DAILY_TRADE_VALUE ( `TRADE_DATE` DATEV2 NOT NULL COMMENT 'Trade Date', `TRADE_ID` VARCHAR(40) NOT NULL COMMENT 'Trade ID', ...... ) AUTO PARTITION BY RANGE (DATE_TRUNC(`TRADE_DATE`, 'month')) () DISTRIBUTED BY HASH(`TRADE_DATE`) BUCKETS 10 PROPERTIES ( ...... );


После импорта некоторых данных мы получаем следующие разделы:


 mysql> show partitions from DAILY_TRADE_VALUE; Empty set (0.10 sec) mysql> insert into DAILY_TRADE_VALUE values ('2015-01-01', 1), ('2020-01-01', 2), ('2024-03-05', 10000), ('2024-03-06', 10001); Query OK, 4 rows affected (0.24 sec) {'label':'label_2a7353a3f991400e_ae731988fa2bc568', 'status':'VISIBLE', 'txnId':'85097'} mysql> show partitions from DAILY_TRADE_VALUE; +-------------+-----------------+----------------+---------------------+--------+--------------+--------------------------------------------------------------------------------+-----------------+---------+----------------+---------------+---------------------+---------------------+--------------------------+----------+------------+-------------------------+-----------+--------------------+--------------+ | PartitionId | PartitionName | VisibleVersion | VisibleVersionTime | State | PartitionKey | Range | DistributionKey | Buckets | ReplicationNum | StorageMedium | CooldownTime | RemoteStoragePolicy | LastConsistencyCheckTime | DataSize | IsInMemory | ReplicaAllocation | IsMutable | SyncWithBaseTables | UnsyncTables | +-------------+-----------------+----------------+---------------------+--------+--------------+--------------------------------------------------------------------------------+-----------------+---------+----------------+---------------+---------------------+---------------------+--------------------------+----------+------------+-------------------------+-----------+--------------------+--------------+ | 588395 | p20150101000000 | 2 | 2024-06-01 19:02:40 | NORMAL | TRADE_DATE | [types: [DATEV2]; keys: [2015-01-01]; ..types: [DATEV2]; keys: [2015-02-01]; ) | TRADE_DATE | 10 | 1 | HDD | 9999-12-31 23:59:59 | | NULL | 0.000 | false | tag.location.default: 1 | true | true | NULL | | 588437 | p20200101000000 | 2 | 2024-06-01 19:02:40 | NORMAL | TRADE_DATE | [types: [DATEV2]; keys: [2020-01-01]; ..types: [DATEV2]; keys: [2020-02-01]; ) | TRADE_DATE | 10 | 1 | HDD | 9999-12-31 23:59:59 | | NULL | 0.000 | false | tag.location.default: 1 | true | true | NULL | | 588416 | p20240301000000 | 2 | 2024-06-01 19:02:40 | NORMAL | TRADE_DATE | [types: [DATEV2]; keys: [2024-03-01]; ..types: [DATEV2]; keys: [2024-04-01]; ) | TRADE_DATE | 10 | 1 | HDD | 9999-12-31 23:59:59 | | NULL | 0.000 | false | tag.location.default: 1 | true | true | NULL | +-------------+-----------------+----------------+---------------------+--------+--------------+--------------------------------------------------------------------------------+-----------------+---------+----------------+---------------+---------------------+---------------------+--------------------------+----------+------------+-------------------------+-----------+--------------------+--------------+ 3 rows in set (0.09 sec)


Как показано, разделы автоматически создаются для импортированных данных и не создаются разделы, выходящие за пределы диапазона существующих данных.

Автоматическое разделение по СПИСКУ

Автоматическое разделение по LIST предназначено для сегментирования данных на основе измерений, не зависящих от времени, таких как region и department . Он заполняет этот пробел для динамического разделения, которое не поддерживает разделение данных по LIST.


Auto Partition от RANGE предоставляет оптимизированное решение для разделения на основе временного измерения. Он более гибок, чем Dynamic Partition, с точки зрения настройки параметров. Синтаксис для него следующий:


 AUTO PARTITION BY LIST (`partition_col`) ()


Это пример автоматического разделения по СПИСКУ с использованием city в качестве столбца разделения:


 mysql> CREATE TABLE `str_table` ( -> `city` VARCHAR NOT NULL, -> ...... -> ) -> DUPLICATE KEY(`city`) -> AUTO PARTITION BY LIST (`city`) -> () -> DISTRIBUTED BY HASH(`city`) BUCKETS 10 -> PROPERTIES ( -> ...... -> ); Query OK, 0 rows affected (0.09 sec) mysql> insert into str_table values ("Denver"), ("Boston"), ("Los_Angeles"); Query OK, 3 rows affected (0.25 sec) mysql> show partitions from str_table; +-------------+-----------------+----------------+---------------------+--------+--------------+-------------------------------------------+-----------------+---------+----------------+---------------+---------------------+---------------------+--------------------------+----------+------------+-------------------------+-----------+--------------------+--------------+ | PartitionId | PartitionName | VisibleVersion | VisibleVersionTime | State | PartitionKey | Range | DistributionKey | Buckets | ReplicationNum | StorageMedium | CooldownTime | RemoteStoragePolicy | LastConsistencyCheckTime | DataSize | IsInMemory | ReplicaAllocation | IsMutable | SyncWithBaseTables | UnsyncTables | +-------------+-----------------+----------------+---------------------+--------+--------------+-------------------------------------------+-----------------+---------+----------------+---------------+---------------------+---------------------+--------------------------+----------+------------+-------------------------+-----------+--------------------+--------------+ | 589685 | pDenver7 | 2 | 2024-06-01 20:12:37 | NORMAL | city | [types: [VARCHAR]; keys: [Denver]; ] | city | 10 | 1 | HDD | 9999-12-31 23:59:59 | | NULL | 0.000 | false | tag.location.default: 1 | true | true | NULL | | 589643 | pLos5fAngeles11 | 2 | 2024-06-01 20:12:37 | NORMAL | city | [types: [VARCHAR]; keys: [Los_Angeles]; ] | city | 10 | 1 | HDD | 9999-12-31 23:59:59 | | NULL | 0.000 | false | tag.location.default: 1 | true | true | NULL | | 589664 | pBoston8 | 2 | 2024-06-01 20:12:37 | NORMAL | city | [types: [VARCHAR]; keys: [Boston]; ] | city | 10 | 1 | HDD | 9999-12-31 23:59:59 | | NULL | 0.000 | false | tag.location.default: 1 | true | true | NULL | +-------------+-----------------+----------------+---------------------+--------+--------------+-------------------------------------------+-----------------+---------+----------------+---------------+---------------------+---------------------+--------------------------+----------+------------+-------------------------+-----------+--------------------+--------------+ 3 rows in set (0.10 sec)


После вставки данных по городам Денвер, Бостон и Лос-Анджелес система автоматически создала соответствующие разделы на основе названий городов. Раньше такого типа пользовательского секционирования можно было добиться только с помощью ручных операторов DDL. Вот как автоматическое разделение по LIST упрощает обслуживание базы данных.

Советы и примечания

Ручная настройка исторических разделов

Для таблиц, которые получают как данные в реальном времени, так и периодические исторические обновления, поскольку Auto Partition не восстанавливает автоматически исторические секции, мы рекомендуем два варианта:


  • Используйте Auto Partition, который будет автоматически создавать разделы для периодических обновлений исторических данных.

  • Используйте автоматическое разделение и вручную создайте раздел LESS THAN для размещения исторических обновлений. Это позволяет более четко разделить исторические данные и данные реального времени и упрощает управление данными.


 mysql> CREATE TABLE DAILY_TRADE_VALUE -> ( -> `TRADE_DATE` DATEV2 NOT NULL COMMENT 'Trade Date', -> `TRADE_ID` VARCHAR(40) NOT NULL COMMENT 'Trade ID' -> ) -> AUTO PARTITION BY RANGE (DATE_TRUNC(`TRADE_DATE`, 'DAY')) -> ( -> PARTITION `pHistory` VALUES LESS THAN ("2024-01-01") -> ) -> DISTRIBUTED BY HASH(`TRADE_DATE`) BUCKETS 10 -> PROPERTIES -> ( -> "replication_num" = "1" -> ); Query OK, 0 rows affected (0.11 sec) mysql> insert into DAILY_TRADE_VALUE values ('2015-01-01', 1), ('2020-01-01', 2), ('2024-03-05', 10000), ('2024-03-06', 10001); Query OK, 4 rows affected (0.25 sec) {'label':'label_96dc3d20c6974f4a_946bc1a674d24733', 'status':'VISIBLE', 'txnId':'85092'} mysql> show partitions from DAILY_TRADE_VALUE; +-------------+-----------------+----------------+---------------------+--------+--------------+--------------------------------------------------------------------------------+-----------------+---------+----------------+---------------+---------------------+---------------------+--------------------------+----------+------------+-------------------------+-----------+--------------------+--------------+ | PartitionId | PartitionName | VisibleVersion | VisibleVersionTime | State | PartitionKey | Range | DistributionKey | Buckets | ReplicationNum | StorageMedium | CooldownTime | RemoteStoragePolicy | LastConsistencyCheckTime | DataSize | IsInMemory | ReplicaAllocation | IsMutable | SyncWithBaseTables | UnsyncTables | +-------------+-----------------+----------------+---------------------+--------+--------------+--------------------------------------------------------------------------------+-----------------+---------+----------------+---------------+---------------------+---------------------+--------------------------+----------+------------+-------------------------+-----------+--------------------+--------------+ | 577871 | pHistory | 2 | 2024-06-01 08:53:49 | NORMAL | TRADE_DATE | [types: [DATEV2]; keys: [0000-01-01]; ..types: [DATEV2]; keys: [2024-01-01]; ) | TRADE_DATE | 10 | 1 | HDD | 9999-12-31 23:59:59 | | NULL | 0.000 | false | tag.location.default: 1 | true | true | NULL | | 577940 | p20240305000000 | 2 | 2024-06-01 08:53:49 | NORMAL | TRADE_DATE | [types: [DATEV2]; keys: [2024-03-05]; ..types: [DATEV2]; keys: [2024-03-06]; ) | TRADE_DATE | 10 | 1 | HDD | 9999-12-31 23:59:59 | | NULL | 0.000 | false | tag.location.default: 1 | true | true | NULL | | 577919 | p20240306000000 | 2 | 2024-06-01 08:53:49 | NORMAL | TRADE_DATE | [types: [DATEV2]; keys: [2024-03-06]; ..types: [DATEV2]; keys: [2024-03-07]; ) | TRADE_DATE | 10 | 1 | HDD | 9999-12-31 23:59:59 | | NULL | 0.000 | false | tag.location.default: 1 | true | true | NULL | +-------------+-----------------+----------------+---------------------+--------+--------------+--------------------------------------------------------------------------------+-----------------+---------+----------------+---------------+---------------------+---------------------+--------------------------+----------+------------+-------------------------+-----------+--------------------+--------------+ 3 rows in set (0.10 sec)


НУЛЕВОЙ раздел

С помощью автоматического разделения по LIST Doris поддерживает сохранение значений NULL в разделах NULL. Например:


 mysql> CREATE TABLE list_nullable -> ( -> `str` varchar NULL -> ) -> AUTO PARTITION BY LIST (`str`) -> () -> DISTRIBUTED BY HASH(`str`) BUCKETS auto -> PROPERTIES -> ( -> "replication_num" = "1" -> ); Query OK, 0 rows affected (0.10 sec) mysql> insert into list_nullable values ('123'), (''), (NULL); Query OK, 3 rows affected (0.24 sec) {'label':'label_f5489769c2f04f0d_bfb65510f9737fff', 'status':'VISIBLE', 'txnId':'85089'} mysql> show partitions from list_nullable; +-------------+---------------+----------------+---------------------+--------+--------------+------------------------------------+-----------------+---------+----------------+---------------+---------------------+---------------------+--------------------------+----------+------------+-------------------------+-----------+--------------------+--------------+ | PartitionId | PartitionName | VisibleVersion | VisibleVersionTime | State | PartitionKey | Range | DistributionKey | Buckets | ReplicationNum | StorageMedium | CooldownTime | RemoteStoragePolicy | LastConsistencyCheckTime | DataSize | IsInMemory | ReplicaAllocation | IsMutable | SyncWithBaseTables | UnsyncTables | +-------------+---------------+----------------+---------------------+--------+--------------+------------------------------------+-----------------+---------+----------------+---------------+---------------------+---------------------+--------------------------+----------+------------+-------------------------+-----------+--------------------+--------------+ | 577297 | pX | 2 | 2024-06-01 08:19:21 | NORMAL | str | [types: [VARCHAR]; keys: [NULL]; ] | str | 10 | 1 | HDD | 9999-12-31 23:59:59 | | NULL | 0.000 | false | tag.location.default: 1 | true | true | NULL | | 577276 | p0 | 2 | 2024-06-01 08:19:21 | NORMAL | str | [types: [VARCHAR]; keys: []; ] | str | 10 | 1 | HDD | 9999-12-31 23:59:59 | | NULL | 0.000 | false | tag.location.default: 1 | true | true | NULL | | 577255 | p1233 | 2 | 2024-06-01 08:19:21 | NORMAL | str | [types: [VARCHAR]; keys: [123]; ] | str | 10 | 1 | HDD | 9999-12-31 23:59:59 | | NULL | 0.000 | false | tag.location.default: 1 | true | true | NULL | +-------------+---------------+----------------+---------------------+--------+--------------+------------------------------------+-----------------+---------+----------------+---------------+---------------------+---------------------+--------------------------+----------+------------+-------------------------+-----------+--------------------+--------------+ 3 rows in set (0.11 sec)


Однако функция автоматического разделения по RANGE не поддерживает разделы NULL, поскольку значения NULL будут храниться в наименьшем разделе LESS THAN , и для него невозможно надежно определить подходящий диапазон. Если бы Auto Partition создал NULL-раздел с диапазоном (-INFINITY, MIN_VALUE), существовал бы риск непреднамеренного удаления этого раздела в рабочей среде, поскольку граница MIN_VALUE может неточно отражать предполагаемую бизнес-логику.

Краткое содержание

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


Прежде чем использовать Auto Partition, важно понимать соответствующие ограничения:


  1. Автоматическое разделение по LIST поддерживает разделение на основе нескольких столбцов , но каждый автоматически созданный раздел содержит только одно значение, а длина имени раздела не может превышать 50 символов. Обратите внимание, что имена разделов соответствуют определенным соглашениям об именах, которые имеют особое значение для управления метаданными. Это означает, что не все 50-символьное пространство находится в распоряжении пользователя.


  2. Функция Auto Partition by RANGE поддерживает только один столбец секции , который должен иметь тип DATE или DATETIME .


  3. Автоматическое разделение по LIST поддерживает столбец раздела NULLABLE и вставку значений NULL. Автоматическое разделение по ДИАПАЗОНУ не поддерживает столбец раздела NULLABLE.


  4. Не рекомендуется использовать автоматическое разделение в сочетании с динамическим разделением после Apache Doris 2.1.3.

Сравнение производительности

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


Динамический раздел использует фиксированные потоки для периодического создания и освобождения разделов. Он поддерживает разделение только по RANGE. Напротив, Auto Partition поддерживает разделение как по RANGE, так и по LIST. Он автоматически создает разделы по требованию на основе определенных правил во время приема данных, обеспечивая более высокий уровень автоматизации и гибкости.


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



Автоматическое разделение: рабочий процесс приема

В этой части рассказывается о том, как прием данных реализуется с помощью механизма автоматического разделения, и в качестве примера мы используем потоковую загрузку . Когда Doris инициирует импорт данных, один из узлов Doris Backend берет на себя роль координатора. Он отвечает за первоначальную обработку данных, а затем отправляет данные на соответствующие узлы BE, известные как исполнители, для выполнения.



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


Для обеспечения этой передачи данных узлы Координатор и Исполнитель устанавливают каналы связи:


  • Отправляющая сторона называется каналом узла.
  • Приемная сторона называется каналом планшетов.


Вот как функция автоматического разделения вступает в игру в процессе определения правильных разделов для данных:



Раньше, без автоматического разделения, когда таблица не имеет требуемого раздела, поведение Doris заключалось в том, что узлы BE накапливали ошибки до тех пор, пока не будет сообщено DATA_QUALITY_ERROR . Теперь, когда автоматическое разделение включено, в Doris Frontend будет инициирован запрос на создание необходимого раздела «на лету». После завершения транзакции создания раздела Doris Frontend отвечает координатору, который затем открывает соответствующие каналы связи (канал узла и канал планшетов) для продолжения процесса приема данных. Это удобный опыт для пользователей.


В реальной кластерной среде время, потраченное координатором на ожидание завершения создания раздела Doris Frontend, может привести к большим накладным расходам. Это связано с задержкой, присущей вызовам Thrift RPC, а также с конфликтами блокировок на внешнем интерфейсе в условиях высокой нагрузки.


Чтобы повысить эффективность приема данных в Auto Partition, Дорис реализовала пакетную обработку, чтобы значительно сократить количество вызовов RPC, выполняемых к FE. Это обеспечивает заметное повышение производительности операций записи данных.


Обратите внимание: когда FE Master завершает транзакцию создания раздела, новый раздел сразу же становится видимым. Однако если процесс импорта в конечном итоге завершится неудачей или будет отменен, созданные разделы не будут автоматически восстановлены.

Производительность автоматического разделения

Мы протестировали производительность и стабильность Auto Partition в Doris, охватывая различные варианты использования:


Случай 1 : 1 интерфейс + 3 серверных; 6 случайно сгенерированных наборов данных, каждый из которых имеет 100 миллионов строк и 2000 разделов; одновременно вставил 6 наборов данных в 6 таблиц


  • Цель : оценить работу Auto Partition под высоким давлением и проверить отсутствие ухудшения производительности.


  • Результаты : Auto Partition приводит к средней потере производительности менее 5% , при этом все транзакции импорта выполняются стабильно.



Случай 2 : 1 интерфейс + 3 серверных; прием 100 строк в секунду из Flink посредством стандартной загрузки; тестирование с 1, 10 и 20 одновременными транзакциями (таблицами) соответственно


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


  • Результаты : с включенным автоматическим разделением или без него прием данных прошел успешно без каких-либо проблем с противодавлением на всех протестированных уровнях параллелизма, даже при 20 одновременных транзакциях, когда загрузка ЦП приближалась к 100%.



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

Заключение и планы на будущее

Начиная с Apache Doris 2.1.0, функция Auto Partition упростила управление DDL и разделами. Он полезен при крупномасштабной обработке данных и позволяет пользователям легко перейти с других систем баз данных на Apache Doris.


Более того, мы стремимся расширить возможности Auto Partition для поддержки более сложных типов данных.


Планы по автоматическому разделению по ДИАПАЗОНУ:


  • Поддержка числовых значений;


  • Разрешение пользователям указывать левую и правую границы диапазона разделов.


Планы по автоматическому разделению по СПИСКУ:


  • Разрешить объединение нескольких значений в один раздел на основе определенных правил.