Todos os produtos
Search
Central de documentação

MaxCompute:Tabela Append - Hash Cluster (Apenas por convite)

Última atualização: Sep 02, 2026

O MaxCompute aprimora o formato Append Delta Table com suporte a Hash Cluster, melhorando o desempenho de consultas e permitindo o processamento incremental de dados. Este tópico aborda as diferenças em relação a outros tipos de tabela, detalhes de sintaxe e exemplos de uso para SQL e o Data Tunnel SDK.

Casos de uso

Recomenda-se o uso do Hash Cluster nos seguintes cenários:

  • Consultas com filtro de igualdade: utilize para buscas pontuais ou filtros de igualdade em colunas específicas e reduza a varredura de dados.

  • Equi-joins e operações GROUP BY: reduza o embaralhamento de dados ao unir ou agregar várias tabelas na mesma chave.

Comparação com tipos de tabela semelhantes

Tipo de tabela

Método de clustering

Gravação incremental (ACID)

Hash Clustered Table

Hash

Não suportado

Oferece otimização de Hash Clustering (Shuffle + Sort), mas não suporta ACID.

PK Delta Table

Hash

Suportado

Fornece capacidades ACID e otimização de Hash Clustering. Adequado para dados com chave primária. Apresenta desempenho de leitura e gravação inferior em comparação a tabelas sem chave primária.

Append Delta Table - Range Cluster

Range

Suportado

Oferece capacidades ACID e suporta recluster, mas o método de clustering Range resulta em desempenho de gravação inferior ao Hash.

Append Delta Table - Hash Cluster

Hash

Suportado

Combina a otimização de Hash Clustering (Shuffle + Sort) com capacidades ACID completas. Também suporta recluster incremental e completo em segundo plano, sendo a opção mais completa em recursos.

Pré-requisitos

Antes de criar uma tabela, ative os seguintes parâmetros de sessão:

SET odps.table.append2.enable=true;
SET odps.table.hash.delta.enable=true; -- Enables the trial feature for creating hash delta tables.

Sintaxe

CREATE TABLE [IF NOT EXISTS] <table_name>
             [(<col_name> <data_type> [comment <col_comment>], ...)]
             [PARTITIONED BY (<col_name> <data_type> [comment <col_comment>], ...)]
             CLUSTERED BY (<col_name> [, <col_name>, ...])
             [SORTED BY (<col_name> [, <col_name>, ...])] -- Only ascending order is supported.
             INTO <number_of_buckets> BUCKETS
             TBLPROPERTIES ('table.format.version' = '2'); 

Parâmetros

Parâmetro

Descrição

CLUSTERED BY

Especifica as colunas de bucketing. Escolha colunas frequentemente usadas em consultas com filtro de igualdade, equi-joins, GROUP BY ou cláusulas WINDOW PARTITION BY. Para obter melhores resultados, selecione colunas com alta cardinalidade para garantir uma distribuição uniforme dos dados entre os buckets.

SORTED BY

Opcional. Define as colunas de ordenação dentro de cada bucket. Atualmente, apenas a ordem crescente é suportada. Recomendamos escolher colunas usadas para filtros de intervalo ou igualdade, cálculos de janela ou timestamps de versionamento.

INTO ... BUCKETS

Define o número de buckets lógicos. Recomendamos definir esse valor com base no volume de dados, na concorrência de consultas e na cardinalidade das colunas de bucketing. A quantidade de buckets afeta o paralelismo das operações de gravação e as otimizações de shuffle durante a leitura.

table.format.version

Defina como 2 para criar uma tabela com o formato de dados necessário para o Hash Cluster.

Exemplos de SQL

Este exemplo utiliza uma tabela de versão de status e preço de product. Na lógica de negócios, buscas pontuais ou joins são normalmente executados em produtos com base no item_id. Portanto, o item_id é designado como a coluna de bucketing por hash. O tempo de vigência da versão, event_time, rastreia alterações históricas; assim, o event_time é definido como a coluna de ordenação. Esse design é adequado para cenários como tabelas de Dimensão de Mudança Lenta (SCD), tabelas de versão de preço de product e tabelas de detalhes de alteração de status.

Preparação

SET odps.sql.type.system.odps2=true;
SET odps.table.append2.enable=true;
SET odps.table.hash.delta.enable=true;

Criar uma tabela

Tabela não particionada

CREATE TABLE hash_delta_sales_demo (
  item_id BIGINT,
  event_time TIMESTAMP,
  price DOUBLE,
  status STRING
)
CLUSTERED BY (item_id)
SORTED BY (event_time)
INTO 256 BUCKETS 
TBLPROPERTIES ('table.format.version' = '2');

Tabela particionada

Para gerenciar dados por data, crie uma tabela particionada:

CREATE TABLE hash_delta_sales_demo_pt (
  item_id BIGINT,
  event_time TIMESTAMP,
  price DOUBLE,
  status STRING
)
PARTITIONED BY (ds STRING)
CLUSTERED BY (item_id)
SORTED BY (event_time)
INTO 256 BUCKETS
TBLPROPERTIES ('table.format.version' = '2');

Execute DESC EXTENDED hash_delta_sales_demo; para visualizar as informações da tabela. As definições de bucketing e ordenação da tabela são as seguintes:

ClusterType:              hash
BucketNum:                256
ClusterColumns:           [item_id]
SortColumns:              [event_time ASC]

Gravações incrementais

Os exemplos a seguir utilizam uma tabela não particionada para demonstrar gravações incrementais e reclustering.

  • Gravação inicial de dados

    INSERT INTO TABLE hash_delta_sales_demo VALUES
      (1001, TIMESTAMP '2026-05-01 10:00:00', 10.00, 'active'),
      (1001, TIMESTAMP '2026-05-03 10:00:00', 13.00, 'active'),
      (1002, TIMESTAMP '2026-05-01 11:00:00', 20.00, 'active');
    
    DESC EXTENDED hash_delta_sales_demo;

    Sample execution result

    Campos principais:

    • DataPhysicalClustered: true -- Os dados estão fisicamente distribuídos em buckets por item_id.

    • DataFullySorted: true — Os dados no bucket estão totalmente ordenados por event_time.

    +------------------------------------------------------------------------------------+
    | Owner:                    ALIYUN$***_com                                           |
    | Project:                  test                                                     |
    | TableComment:                                                                      |
    +------------------------------------------------------------------------------------+
    | CreateTime:               2026-07-08 16:38:34                                      |
    | LastDDLTime:              2026-07-08 16:38:34                                      |
    | LastModifiedTime:         2026-07-08 16:39:38                                      |
    +------------------------------------------------------------------------------------+
    | InternalTable: YES      | Size: 4823                                               |
    +------------------------------------------------------------------------------------+
    | Native Columns:                                                                    |
    +------------------------------------------------------------------------------------+
    | Field    | Type   | Label | ExtendedLabel | Nullable | DefaultValue | Comment      |
    +------------------------------------------------------------------------------------+
    | item_id  | bigint |       |               | true     | NULL         |              |
    | event_time | timestamp |  |               | true     | NULL         |              |
    | price    | double |       |               | true     | NULL         |              |
    | status   | string |       |               | true     | NULL         |              |
    +------------------------------------------------------------------------------------+
    | Extended Info:                                                                     |
    +------------------------------------------------------------------------------------+
    | TableID:                  65**8e                                                   |
    | IsArchived:               false                                                    |
    | PhysicalSize:             14469                                                    |
    | FileNum:                  5                                                        |
    | ColdStorageStatus:        N/A                                                      |
    | CompressionStrategy:      normal                                                   |
    | DataFullySorted:          true                                                     |
    | DataPhysicalClustered:    true                                                     |
    | IsolationMin:             NONSTRICT_SNAPSHOT_ISOLATION                             |
    | OverlapDepth:             2                                                        |
    | OverlapRatio:             1.000000                                                 |
    | StoredAs:                 AliOrc                                                   |
    | Transactional:            true                                                     |
    | encryption_enable:        false                                                    |
    | odps.timemachine.retention.days: 1                                                        |
    | ClusterType:              hash                                                     |
    | BucketNum:                256                                                      |
    | ClusterColumns:           [item_id]                                                |
    | SortColumns:              [event_time ASC]                                         |
    | StorageTier:              Standard                                                 |
    | StorageTierLastModifiedTime:  2026-07-08 16:39:38                                  |
    +------------------------------------------------------------------------------------+
  • Operação de exclusão

    Verifique o status da tabela após excluir alguns dados:

    DELETE FROM hash_delta_sales_demo WHERE item_id = 1002;
    
    DESC EXTENDED hash_delta_sales_demo;

    Sample execution result

    Campos principais:

    • DataPhysicalClustered: true -- Os dados estão fisicamente agrupados por item_id.

    • DataFullySorted: true -- Operações de exclusão não afetam a ordem de classificação dos dados existentes.

    +------------------------------------------------------------------------------------+
    | Owner:                    ALIYUN$***_com                                           |
    | Project:                  test                                                     |
    | TableComment:                                                                      |
    +------------------------------------------------------------------------------------+
    | CreateTime:               2026-07-08 16:38:34                                      |
    | LastDDLTime:              2026-07-08 16:38:34                                      |
    | LastModifiedTime:         2026-07-08 16:41:02                                      |
    | LastAccessTime:           2026-07-08 16:40:57                                      |
    +------------------------------------------------------------------------------------+
    | InternalTable: YES      | Size: 7082                                               |
    +------------------------------------------------------------------------------------+
    | Native Columns:                                                                    |
    +------------------------------------------------------------------------------------+
    | Field    | Type   | Label | ExtendedLabel | Nullable | DefaultValue | Comment      |
    +------------------------------------------------------------------------------------+
    | item_id  | bigint |       |               | true     | NULL         |              |
    | event_time | timestamp |  |               | true     | NULL         |              |
    | price    | double |       |               | true     | NULL         |              |
    | status   | string |       |               | true     | NULL         |              |
    +------------------------------------------------------------------------------------+
    | Extended Info:                                                                     |
    +------------------------------------------------------------------------------------+
    | TableID:                  65**8e                                                   |
    | IsArchived:               false                                                    |
    | PhysicalSize:             21246                                                    |
    | FileNum:                  10                                                       |
    | ColdStorageStatus:        N/A                                                      |
    | CompressionStrategy:      normal                                                   |
    | DataFullySorted:          true                                                     |
    | DataPhysicalClustered:    true                                                     |
    | IsolationMin:             NONSTRICT_SNAPSHOT_ISOLATION                             |
    | OverlapDepth:             2                                                        |
    | OverlapRatio:             1.000000                                                 |
    | StoredAs:                 AliOrc                                                   |
    | Transactional:            true                                                     |
    | encryption_enable:        false                                                    |
    | odps.timemachine.retention.days: 1                                                        |
    | ClusterType:              hash                                                     |
    | BucketNum:                256                                                      |
    | ClusterColumns:           [item_id]                                                |
    | SortColumns:              [event_time ASC]                                         |
    | StorageTier:              Standard                                                 |
    | StorageTierLastModifiedTime:  2026-07-08 16:41:02                                  |
    +------------------------------------------------------------------------------------+
    
  • Backfill de dados históricos

    Faça o backfill de uma versão histórica. O event_time desta versão situa-se entre os timestamps existentes para item_id=1001:

    INSERT INTO TABLE hash_delta_sales_demo VALUES
      (1001, TIMESTAMP '2026-05-02 09:00:00', 12.00, 'active');
    
    DESC EXTENDED hash_delta_sales_demo;

    Sample execution result

    Campos principais:

    • DataPhysicalClustered: true -- Os dados estão fisicamente agrupados por item_id.

    • DataFullySorted: false -- Os dados no bucket não estão mais totalmente ordenados porque novos arquivos foram adicionados.

    +------------------------------------------------------------------------------------+
    | Owner:                    ALIYUN$***_com                                           |
    | Project:                  test                                                     |
    | TableComment:                                                                      |
    +------------------------------------------------------------------------------------+
    | CreateTime:               2026-07-08 16:38:34                                      |
    | LastDDLTime:              2026-07-08 16:38:34                                      |
    | LastModifiedTime:         2026-07-08 16:42:54                                      |
    | LastAccessTime:           2026-07-08 16:40:57                                      |
    +------------------------------------------------------------------------------------+
    | InternalTable: YES      | Size: 10705                                              |
    +------------------------------------------------------------------------------------+
    | Native Columns:                                                                    |
    +------------------------------------------------------------------------------------+
    | Field    | Type   | Label | ExtendedLabel | Nullable | DefaultValue | Comment      |
    +------------------------------------------------------------------------------------+
    | item_id  | bigint |       |               | true     | NULL         |              |
    | event_time | timestamp |       |               | true     | NULL         |              |
    | price    | double |       |               | true     | NULL         |              |
    | status   | string |       |               | true     | NULL         |              |
    +------------------------------------------------------------------------------------+
    | Extended Info:                                                                     |
    +------------------------------------------------------------------------------------+
    | TableID:                  65**8e                                                   |
    | IsArchived:               false                                                    |
    | PhysicalSize:             32115                                                    |
    | FileNum:                  13                                                       |
    | ColdStorageStatus:        N/A                                                      |
    | CompressionStrategy:      normal                                                   |
    | DataFullySorted:          false                                                    |
    | DataPhysicalClustered:    true                                                     |
    | IsolationMin:             NONSTRICT_SNAPSHOT_ISOLATION                             |
    | OverlapDepth:             2                                                        |
    | OverlapRatio:             1.000000                                                 |
    | StoredAs:                 AliOrc                                                   |
    | Transactional:            true                                                     |
    | encryption_enable:        false                                                    |
    | odps.timemachine.retention.days: 1                                                        |
    | ClusterType:              hash                                                     |
    | BucketNum:                256                                                      |
    | ClusterColumns:           [item_id]                                                |
    | SortColumns:              [event_time ASC]                                         |
    | StorageTier:              Standard                                                 |
    | StorageTierLastModifiedTime:  2026-07-08 16:42:54                                  |
    +------------------------------------------------------------------------------------+
    

Bucket pruning

Ao executar uma consulta com filtro de igualdade em uma coluna de bucketing, o MaxCompute usa a distribuição hash para localizar diretamente o bucket alvo e evita a varredura dos demais buckets. A consulta a seguir filtra por item_id = 1001 e lê apenas o bucket lógico que contém esse valor, evitando uma varredura completa da tabela:

SELECT * FROM hash_delta_sales_demo
  WHERE item_id = 1001
  ORDER BY event_time
  LIMIT 10;

-- Returns:
+------------+---------------------+------------+--------+
| item_id    | event_time          | price      | status |
+------------+---------------------+------------+--------+
| 1001       | 2026-05-01 10:00:00 | 10.0       | active |
| 1001       | 2026-05-02 09:00:00 | 12.0       | active |
| 1001       | 2026-05-03 10:00:00 | 13.0       | active |
+------------+---------------------+------------+--------+

Recluster completo

Para reorganizar os dados existentes, execute RECLUSTER FULL. Essa operação preserva a semântica dos dados históricos na tabela e reorganiza os dados armazenados conforme a definição atual da tabela.

ALTER TABLE hash_delta_sales_demo RECLUSTER FULL;

DESC EXTENDED hash_delta_sales_demo;

Sample execution result

Campos principais:

  • DataPhysicalClustered: true -- Os dados estão fisicamente distribuídos em buckets por item_id.

  • DataFullySorted: true — Todos os dados no bucket estão ordenados.

+------------------------------------------------------------------------------------+
| Owner:                    ALIYUN$***_com                                           |
| Project:                  test                                                     |
| TableComment:                                                                      |
+------------------------------------------------------------------------------------+
| CreateTime:               2026-07-08 16:38:34                                      |
| LastDDLTime:              2026-07-08 16:38:34                                      |
| LastModifiedTime:         2026-07-08 16:42:54                                      |
| LastAccessTime:           2026-07-08 16:40:57                                      |
+------------------------------------------------------------------------------------+
| InternalTable: YES      | Size: 20218                                              |
+------------------------------------------------------------------------------------+
| Native Columns:                                                                    |
+------------------------------------------------------------------------------------+
| Field    | Type   | Label | ExtendedLabel | Nullable | DefaultValue | Comment      |
+------------------------------------------------------------------------------------+
| item_id  | bigint |       |               | true     | NULL         |              |
| event_time | timestamp |       |               | true     | NULL         |              |
| price    | double |       |               | true     | NULL         |              |
| status   | string |       |               | true     | NULL         |              |
+------------------------------------------------------------------------------------+
| Extended Info:                                                                     |
+------------------------------------------------------------------------------------+
| TableID:                  65**8e                                                   |
| IsArchived:               false                                                    |
| PhysicalSize:             60654                                                    |
| FileNum:                  20                                                       |
| ColdStorageStatus:        N/A                                                      |
| CompressionStrategy:      normal                                                   |
| DataFullySorted:          true                                                     |
| DataPhysicalClustered:    true                                                     |
| IsolationMin:             NONSTRICT_SNAPSHOT_ISOLATION                             |
| OverlapDepth:             2                                                        |
| OverlapRatio:             1.000000                                                 |
| StoredAs:                 AliOrc                                                   |
| Transactional:            true                                                     |
| encryption_enable:        false                                                    |
| odps.timemachine.retention.days: 1                                                        |
| ClusterType:              hash                                                     |
| BucketNum:                256                                                      |
| ClusterColumns:           [item_id]                                                |
| SortColumns:              [event_time ASC]                                         |
| StorageTier:              Standard                                                 |
| StorageTierLastModifiedTime:  2026-07-08 16:42:54                                  |
+------------------------------------------------------------------------------------+

Uma Append Delta Table com Hash Cluster suporta operações de gravação incremental como INSERT, UPDATE, DELETE e MERGE INTO, preservando a distribuição hash. O otimizador escolhe um plano de execução com base no estado atual dos dados. Ele aproveita o armazenamento ordenado quando os dados estão classificados e recorre ao bucketing por hash quando os dados não estão totalmente ordenados. Execute RECLUSTER FULL a qualquer momento para restaurar a ordem de classificação completa.

Exemplo do Data Tunnel SDK

Esta seção mostra como usar o Data Tunnel SDK para carregar e baixar dados da tabela hash_delta_sales_demo.

  1. Importe a dependência do SDK

    Use a versão 0,59 ou posterior. Para mais detalhes, consulte as Notas de Lançamento.

  2. Código de exemplo

    Sample code

    /**
     * This example demonstrates how to upload and download data from the hash_delta_sales_demo table
     * by using MaxStorageClient.
     *
     * Table Schema:
     * CREATE TABLE hash_delta_sales_demo (
     *   item_id BIGINT,
     *   event_time TIMESTAMP,
     *   price DOUBLE,
     *   status STRING
     * );
     */
    public class MaxStorageClientExample {
    
        private static final String ENDPOINT = "<your-endpoint>";
        private static final String TUNNEL_ENDPOINT = "<your-tunnel-endpoint>";
        private static final String PROJECT = "<your-project>";
        private static final String ACCESS_ID = "<your-access-id>";
        private static final String ACCESS_KEY = "<your-access-key>";
        private static final String TABLE_NAME = "hash_delta_sales_demo";
    
        public static void main(String[] args) throws Exception {
            RootAllocator allocator = new RootAllocator(Long.MAX_VALUE);
    
            // 1. Build the MaxStorageClient.
            MaxStorageClient client = MaxStorageClient.builder()
                    .endpoint(ENDPOINT)
                    .tunnelEndpoint(TUNNEL_ENDPOINT)
                    .credentialsProvider(
                            new StaticCredentialProvider(new AliyunAccount(ACCESS_ID, ACCESS_KEY).getCredentials()))
                    .project(PROJECT)
                    .bufferAllocator(allocator)
                    .build();
    
            try {
                // 2. Upload data.
                uploadData(client);
                Thread.sleep(5000);
    
                // 3. Download data.
                downloadData(client);
            } finally {
                client.close();
                allocator.close();
            }
        }
    
        /**
         * Upload data to the hash_delta_sales_demo table.
         */
        private static void uploadData(MaxStorageClient client) throws Exception {
            TableIdentifier tableId = TableIdentifier.of(PROJECT, TABLE_NAME);
    
            // Create a write session. withOverwrite(true) indicates that the table will be overwritten.
            TableWriteSession writeSession = client
                    .createTableWriteSessionBuilder(tableId)
                    .withOverwrite(true)
                    .build();
    
            System.out.println("Write session created: " + writeSession.getId());
    
            // Use RecordWriter to write data (a high-level API for row-based writing).
            try (RecordWriter writer = writeSession.createWriterBuilder("stream-1", 1)
                    .build()
                    .getAsRecordWriter(1024)) {
    
                for (int i = 0; i < 1000; i++) {
                    Record record = writer.newRecord(false);
                    record.set(0, (long) i);                                    // item_id: BIGINT
                    record.set(1, LocalDateTime.of(2025, 5, 18, 10, 30, i % 60).atZone(ZoneId.systemDefault())
                            .toInstant()); // event_time: TIMESTAMP
                    record.set(2, 99.9 + i * 0.1);                             // price: DOUBLE
                    record.set(3, i % 2 == 0 ? "paid" : "pending");            // status: STRING
                    writer.write(record);
                }
            }
    
            // Commit the session to make the data visible.
            writeSession.commit();
            System.out.println("Successfully uploaded 1000 records to " + TABLE_NAME);
        }
    
        /**
         * Download data from the hash_delta_sales_demo table.
         */
        private static void downloadData(MaxStorageClient client) throws Exception {
            TableIdentifier tableId = TableIdentifier.of(PROJECT, TABLE_NAME);
    
            // Create a read session. You can select specific columns and apply filters.
            TableReadSession readSession = client.createTableReadSessionBuilder(tableId)
                    .withColumns(Arrays.asList("item_id", "event_time", "price", "status"))
                    .withSplitOptions(SplitOptions.newBuilder()
                            .withSplitMode(SplitMode.ROW_OFFSET)
                            .build())
                    .build();
    
            System.out.println("Read session created: " + readSession.getId());
    
            // Get the input splits.
            List<InputSplit> splits = readSession.getSplits();
            System.out.println("Total splits: " + splits.size());
    
            int totalRecords = 0;
    
            // Iterate through each split to read data.
            for (InputSplit split : splits) {
                try (ArrowReader reader = readSession.createReaderBuilder(split).build()) {
                    Schema schema = reader.getSchema();
                    System.out.println("Schema: " + schema);
    
                    while (reader.nextBatch()) {
                        VectorSchemaRoot root = reader.getCurrentValue();
                        int rowCount = root.getRowCount();
                        totalRecords += rowCount;
    
                        // Print the first 5 rows as an example.
                        int printCount = Math.min(rowCount, 5);
                        for (int i = 0; i < printCount; i++) {
                            System.out.printf("  item_id=%s, event_time=%s, price=%s, status=%s%n",
                                    root.getVector("item_id").getObject(i),
                                    root.getVector("event_time").getObject(i),
                                    root.getVector("price").getObject(i),
                                    root.getVector("status").getObject(i));
                        }
                        if (rowCount > 5) {
                            System.out.println("  ... (" + (rowCount - 5) + " more rows in this batch)");
                        }
                    }
                }
            }
    
            System.out.println("Total records downloaded: " + totalRecords);
        }
    }

FAQ

Escolha do tamanho de armazenamento do bucket

O tamanho de armazenamento recomendado para um único bucket varia de algumas centenas de megabytes a dezenas de gigabytes.

  • Buckets pequenos aumentam a sobrecarga de armazenamento e os custos de shuffle.

  • Buckets grandes prolongam o tempo de gravação e reduzem a eficácia do Bucket Pruning e das otimizações de shuffle.

Defina o número de buckets com base no crescimento esperado dos dados, e não apenas no volume atual, para evitar modificações frequentes na estrutura da tabela.

Se o volume de dados for excepcionalmente grande, um único bucket pode suportar mais armazenamento ou você pode definir um número maior de buckets. No entanto, avalie o impacto no desempenho de gravação e consulta com base no seu caso de uso específico.