paint-brush
Evolução da fragmentação de dados em direção à automação e flexibilidade no Apache Dorispor@frankzzz
Novo histórico

Evolução da fragmentação de dados em direção à automação e flexibilidade no Apache Doris

por Frank Z29m2024/07/20
Read on Terminal Reader

Muito longo; Para ler

Apache Doris apresenta partição automática na V2.1.0. Ele simplificou o gerenciamento de DDL e partição desde o Apache Doris 2.1.0. É útil no processamento de dados em larga escala e facilita a migração dos usuários de outros sistemas de banco de dados para o Apache Doris.
featured image - Evolução da fragmentação de dados em direção à automação e flexibilidade no Apache Doris
Frank Z HackerNoon profile picture


Para lidar com grandes conjuntos de dados, os bancos de dados distribuídos introduzem estratégias como particionamento e bucketing. Os dados são divididos em unidades menores com base em regras específicas e distribuídos em diferentes nós para que os bancos de dados possam realizar processamento paralelo para maior desempenho e flexibilidade de gerenciamento de dados.


Como em muitos bancos de dados, o Apache Doris fragmenta os dados em partições e, em seguida, uma partição é dividida em buckets. As partições são normalmente definidas por tempo ou outros valores contínuos. Isso permite que os mecanismos de consulta localizem rapidamente os dados de destino durante as consultas, eliminando intervalos de dados irrelevantes.


Bucketing , por outro lado, distribui dados com base nos valores hash de uma ou mais colunas, o que evita distorção de dados.


Antes da versão 2.1.0 , havia duas maneiras de criar partições de dados no Apache Doris:


  • Partição manual : os usuários especificam as partições na instrução de criação da tabela ou as modificam posteriormente por meio de instruções DDL.


  • Partição Dinâmica : O sistema mantém partições automaticamente dentro de um intervalo predefinido com base no tempo de ingestão de dados.


No Apache Doris 2.1.0, introduzimos Auto Partition . Ele suporta particionamento de dados por RANGE ou por LIST e aumenta ainda mais a flexibilidade além do particionamento automático.

Evolução das estratégias de particionamento em Doris

No design da distribuição de dados, nos concentramos mais no planejamento de partições porque a escolha de colunas de partição e intervalos de partição depende muito dos padrões reais de distribuição de dados, e um bom design de partição pode melhorar amplamente a eficiência de consulta e armazenamento da tabela.


No Doris, a tabela de dados é dividida em partições e depois em intervalos de maneira hierárquica. Os dados dentro do mesmo bucket formam um tablet de dados, que é a unidade mínima de armazenamento físico no Doris para replicação de dados, agendamento de dados entre clusters e balanceamento de carga.


Partição manual

Doris permite aos usuários criar manualmente partições de dados por RANGE e por LIST.


Para dados com carimbo de data/hora, como logs e registros de transações, os usuários normalmente criam partições com base na dimensão de tempo. Aqui está um exemplo da instrução 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" );


A tabela é particionada pela date de importação de dados e 4 partições foram pré-criadas. Dentro de cada partição, os dados são divididos em 16 buckets com base no valor hash do user_id .


Com esse design de particionamento e bucket, ao consultar dados de 2018 em diante, o sistema só precisa verificar a partição p2018 . Esta é a aparência da consulta 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 | | | +--------------------------------------------------------------------------------------+


Se os dados forem distribuídos de forma desigual entre as partições, o mecanismo de bucket baseado em hash poderá dividir ainda mais os dados com base no user_id . Isto ajuda a evitar o desequilíbrio de carga em algumas máquinas durante a consulta e o armazenamento.


No entanto, em cenários de negócios reais, um cluster pode ter dezenas de milhares de tabelas, o que significa que é impossível gerenciá-las manualmente.


 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 ( ...... );


No exemplo acima, os dados são particionados mensalmente. Isso exige que o administrador do banco de dados (DBA) adicione manualmente uma nova partição a cada mês e mantenha o esquema da tabela regularmente. Imagine o caso do processamento de dados em tempo real, onde você pode precisar criar partições diariamente ou mesmo de hora em hora. Fazer isso manualmente não é mais uma escolha. É por isso que introduzimos a Partição Dinâmica.

Partição Dinâmica

Pela Partição Dinâmica, Doris cria e recupera automaticamente partições de dados, desde que o usuário especifique a unidade de partição, o número de partições históricas e o número de partições futuras. Esta funcionalidade depende de um thread fixo no Doris Frontend. Ele pesquisa e verifica continuamente novas partições a serem criadas ou partições antigas a serem recuperadas e atualiza o esquema de partição da tabela.


Este é um exemplo de instrução CREATE TABLE para uma tabela particionada por dia. Os parâmetros start e end são definidos como -7 e 3 , respectivamente, o que significa que as partições de dados para os próximos 3 dias serão pré-criadas e as partições históricas com mais de 7 dias serão recuperadas.


 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" );


Com o tempo, a tabela sempre manterá partições dentro do intervalo [current date - 7, current date + 3] . A Partição Dinâmica é particularmente útil para cenários de ingestão de dados em tempo real, como quando a camada ODS (Operational Data Store) recebe dados diretamente de fontes externas como Kafka.


Os parâmetros start e end definem um intervalo fixo para as partições, permitindo ao usuário gerenciar as partições apenas dentro deste intervalo. No entanto, se o usuário precisar incluir mais dados históricos, ele terá que discar o valor start , o que poderá levar a uma sobrecarga desnecessária de metadados no cluster.


Portanto, ao aplicar a Partição Dinâmica, existe um compromisso entre a conveniência e a eficiência do gerenciamento de metadados.

Palavras dos desenvolvedores

À medida que a complexidade dos negócios aumenta, a Partição Dinâmica torna-se inadequada porque:


  • Ele suporta particionamento apenas por RANGE, mas não por LIST.
  • Ele só pode ser aplicado aos carimbos de data/hora atuais do mundo real.
  • Ele oferece suporte apenas a um único intervalo de partição contínua e não pode acomodar partições fora desse intervalo.


Dadas essas limitações funcionais, começamos a planejar um novo mecanismo de particionamento que pudesse automatizar o gerenciamento de partições e simplificar a manutenção da tabela de dados.


Descobrimos que a implementação ideal de particionamento deveria:


  • Evite a necessidade de criar partições manualmente após a criação da tabela;
  • Ser capaz de acomodar todos os dados ingeridos nas partições correspondentes.


O primeiro significa automação e o segundo flexibilidade. A essência de realizar ambos é associar a criação de partições aos dados reais.


Então começamos a pensar no seguinte: e se adiarmos a criação de partições até que os dados sejam ingeridos, em vez de fazê-lo durante a criação da tabela ou por meio de pesquisas regulares? Em vez de pré-construir a distribuição de partições, podemos definir as regras de mapeamento "dados para partição" para que as partições sejam criadas após a chegada dos dados.


Comparado à Partição Manual, todo esse processo seria totalmente automatizado, eliminando a necessidade de manutenção humana. Comparado à Partição Dinâmica, evita partições que não são usadas ou partições que são necessárias, mas não estão presentes.

Partição automática

Com Apache Doris 2.1.0 , concretizamos o plano acima. Durante a ingestão de dados, Doris cria partições de dados com base nas regras configuradas. Os nós Doris Backend responsáveis pelo processamento e distribuição de dados tentarão encontrar a partição apropriada para cada linha de dados no operador DataSink do plano de execução. Ele não filtra mais dados que não cabem em nenhuma partição existente ou relata um erro para tal situação, mas gera automaticamente partições para todos os dados ingeridos.

Partição automática por RANGE

O Auto Partition by RANGE fornece uma solução de particionamento otimizada com base na dimensão de tempo. É mais flexível que a Partição Dinâmica em termos de configuração de parâmetros. A sintaxe para isso é a seguinte:


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


A <partition_column> acima é a coluna de partição (ou seja, a coluna na qual o particionamento se baseia). <interval> especifica a unidade de partição, que é a largura desejada de cada partição.


Por exemplo, se a coluna de partição for k0 e você desejar particionar por mês, a instrução de partição seria AUTO PARTITION BY RANGE (DATE_TRUNC(k0, 'month')) . Para todos os dados importados, o sistema chamará DATE_TRUNC(k0, 'month') para calcular o ponto final esquerdo da partição e, em seguida, o ponto final direito adicionando um interval .


Agora, podemos aplicar a Partição Automática à tabela DAILY_TRADE_VALUE apresentada na seção anterior sobre Partição Dinâmica.


 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 ( ...... );


Depois de importar alguns dados, estas são as partições que obtemos:


 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)


Como é mostrado, as partições são criadas automaticamente para os dados importados e não criam partições que estejam além do intervalo dos dados existentes.

Partição automática por LIST

A partição automática por LIST serve para fragmentar dados com base em dimensões não baseadas em tempo, como region e department . Ele preenche essa lacuna para a Partição Dinâmica, que não oferece suporte ao particionamento de dados por LIST.


O Auto Partition by RANGE fornece uma solução de particionamento otimizada com base na dimensão de tempo. É mais flexível que a Partição Dinâmica em termos de configuração de parâmetros. A sintaxe para isso é a seguinte:


 AUTO PARTITION BY LIST (`partition_col`) ()


Este é um exemplo de partição automática por LIST usando city como coluna de partição:


 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)


Após inserir os dados das cidades de Denver, Boston e Los Angeles, o sistema criou automaticamente partições correspondentes com base nos nomes das cidades. Anteriormente, esse tipo de particionamento personalizado só podia ser obtido por meio de instruções DDL manuais. É assim que o Auto Partition by LIST simplifica a manutenção do banco de dados.

Dicas e notas

Ajustar manualmente as partições históricas

Para tabelas que recebem dados em tempo real e atualizações históricas ocasionais, uma vez que a Partição Automática não recupera partições históricas automaticamente, recomendamos duas opções:


  • Use a partição automática, que criará partições automaticamente para atualizações ocasionais de dados históricos.

  • Use a partição automática e crie manualmente uma partição LESS THAN para acomodar as atualizações históricas. Isso permite uma separação mais clara entre dados históricos e em tempo real e facilita o gerenciamento de dados.


 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)


Partição NULA

Com a partição automática por LIST, Doris suporta o armazenamento de valores NULL em partições NULL. Por exemplo:


 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)


No entanto, a partição automática por RANGE não oferece suporte a partições NULL, porque os valores NULL serão armazenados na menor partição LESS THAN e é impossível determinar com segurança o intervalo apropriado para ela. Se a Partição Automática criasse uma partição NULL com um intervalo de (-INFINITY, MIN_VALUE), haveria o risco desta partição ser excluída inadvertidamente na produção, pois o limite MIN_VALUE pode não representar com precisão a lógica de negócios pretendida.

Resumo

A Partição Automática cobre a maioria dos casos de uso da Partição Dinâmica, ao mesmo tempo que introduz o benefício da definição antecipada de regras de partição. Depois que as regras são definidas, a maior parte do trabalho de criação de partições é realizada automaticamente por Doris em vez de por um DBA.


Antes de utilizar a Partição Automática, é importante compreender as limitações relevantes:


  1. A partição automática por LIST oferece suporte ao particionamento com base em várias colunas , mas cada partição criada automaticamente contém apenas um único valor e o nome da partição não pode exceder 50 caracteres de comprimento. Observe que os nomes das partições seguem convenções de nomenclatura específicas, que têm implicações específicas para o gerenciamento de metadados. Isso significa que nem todo o espaço de 50 caracteres está à disposição do usuário.


  2. A Partição Automática por RANGE suporta apenas uma única coluna de partição , que deve ser do tipo DATE ou DATETIME .


  3. A partição automática por LIST suporta coluna de partição NULLABLE e inserção de valores NULL. A partição automática por RANGE não oferece suporte à coluna de partição NULLABLE.


  4. Não é recomendado usar a Partição Automática em conjunto com a Partição Dinâmica após o Apache Doris 2.1.3.

Comparação de desempenho

As principais diferenças funcionais entre a Partição Automática e a Partição Dinâmica estão na criação e exclusão de partições, nos tipos de partição suportados e em seu impacto no desempenho de importação.


A partição dinâmica usa threads fixos para criar e recuperar partições periodicamente. Ele suporta apenas particionamento por RANGE. Por outro lado, a Partição Automática suporta particionamento por RANGE e por LIST. Ele cria automaticamente partições sob demanda com base em regras específicas durante a ingestão de dados, proporcionando um nível mais alto de automação e flexibilidade.


A Partição Dinâmica não diminui a velocidade de ingestão de dados, enquanto a Partição Automática causa certas sobrecargas de tempo porque primeiro verifica as partições existentes e depois cria novas sob demanda. Apresentaremos os resultados dos testes de desempenho.



Partição automática: fluxo de trabalho de ingestão

Esta parte é sobre como a ingestão de dados é implementada com o mecanismo de Partição Automática e usamos Stream Load como exemplo. Quando Doris inicia uma importação de dados, um dos nós Doris Backend assume a função de Coordenador. Ele é responsável pelo trabalho inicial de processamento de dados e, em seguida, despacha os dados para os nós BE apropriados, conhecidos como Executores, para execução.



No nó Datasink final do pipeline de execução do coordenador, os dados precisam ser roteados para as partições, buckets e locais corretos do nó Doris Backend antes que possam ser transmitidos e armazenados com êxito.


Para possibilitar esta transferência de dados, os nós Coordenador e Executor estabelecem canais de comunicação:


  • A extremidade de envio é chamada de Canal do Nó.
  • A extremidade receptora é chamada de Canal Tablets.


É assim que a Partição Automática entra em ação durante o processo de determinação das partições corretas para os dados:



Anteriormente, sem a Partição Automática, quando uma tabela não possui a partição necessária, o comportamento no Doris era os nós BE acumularem erros até que um DATA_QUALITY_ERROR fosse relatado. Agora, com a partição automática habilitada, uma solicitação será iniciada ao Doris Frontend para criar a partição necessária instantaneamente. Após a conclusão da transação de criação da partição, o Doris Frontend responde ao Coordenador, que então abre os canais de comunicação correspondentes (Canal Node e Canal Tablets) para continuar o processo de ingestão de dados. Esta é uma experiência perfeita para os usuários.


Num ambiente de cluster do mundo real, o tempo gasto pelo Coordenador à espera que o Doris Frontend conclua a criação da partição pode incorrer em grandes despesas gerais. Isso se deve à latência inerente das chamadas RPC do Thrift, bem como à contenção de bloqueio no Frontend sob condições de alta carga.


Para melhorar a eficiência da ingestão de dados na partição automática, Doris implementou lotes para reduzir amplamente o número de chamadas RPC feitas ao FE. Isso traz um aprimoramento notável de desempenho para operações de gravação de dados.


Observe que quando o FE Master conclui a transação de criação da partição, a nova partição torna-se imediatamente visível. No entanto, se o processo de importação falhar ou for cancelado, as partições criadas não serão recuperadas automaticamente.

Desempenho da partição automática

Testamos o desempenho e a estabilidade da partição automática no Doris, cobrindo diferentes casos de uso:


Caso 1 : 1 Frontend + 3 Backend; 6 conjuntos de dados gerados aleatoriamente, cada um com 100 milhões de linhas e 2.000 partições; ingeriu os 6 conjuntos de dados simultaneamente em 6 tabelas


  • Objetivo : Avaliar o desempenho da Partição Automática sob alta pressão e verificar qualquer degradação de desempenho.


  • Resultados : a partição automática traz uma perda média de desempenho inferior a 5% , com todas as transações de importação funcionando de forma estável.



Caso 2 : 1 Frontend + 3 Backend; ingerindo 100 linhas por segundo do Flink por carregamento de rotina; testando com 1, 10 e 20 transações simultâneas (tabelas), respectivamente


  • Objetivo : identificar possíveis problemas de backlog de dados que possam surgir com a partição automática em diferentes níveis de simultaneidade.


  • Resultados : com ou sem partição automática habilitada, a ingestão de dados foi bem-sucedida sem problemas de contrapressão em todos os níveis de simultaneidade testados, mesmo em 20 transações simultâneas quando a utilização da CPU atingiu perto de 100%.



Para concluir os resultados destes testes, o impacto da ativação da Partição Automática no desempenho da ingestão de dados é mínimo.

Conclusão e planos futuros

A partição automática simplificou o DDL e o gerenciamento de partições desde o Apache Doris 2.1.0. É útil no processamento de dados em larga escala e facilita a migração dos usuários de outros sistemas de banco de dados para o Apache Doris.


Além disso, estamos empenhados em expandir as capacidades da Partição Automática para suportar tipos de dados mais complexos.


Planos para partição automática por RANGE:


  • Suporta valores numéricos;


  • Permitindo que os usuários especifiquem os limites esquerdo e direito do intervalo de partição.


Planos para partição automática por LIST:


  • Permitir mesclar vários valores na mesma partição com base em regras específicas.