Todos os produtos
Search
Central de documentação

ApsaraDB for SelectDB:Group Commit

Última atualização: Jul 10, 2026

Escritas de dados em alta frequência — como pipelines de log ou streams de eventos de IoT — geram dois problemas quando cada escrita é uma importação separada: cada operação incorre em sua própria sobrecarga de transação (análise de SQL, geração de plano) e cria uma nova versão da tabela, acelerando a pressão de compactação em segundo plano. O recurso group commit resolve ambos os problemas ao mesclar várias operações INSERT INTO VALUES, Stream Load ou HTTP Stream no lado do servidor em uma única transação interna. Isso reduz a sobrecarga de I/O e melhora o throughput de escrita sem exigir lógica de agrupamento no cliente.

Como funciona

O group commit não é um método de importação separado. Ele intercepta requisições qualificadas de INSERT INTO VALUES, Stream Load e HTTP Stream e as agrupa em um único commit interno. O commit automático é acionado quando qualquer um dos seguintes limites é atingido:

  • O intervalo de commit expira (padrão: 10 segundos)

  • O tamanho acumulado dos dados atinge o limite (padrão: 64 MB)

Três modos controlam esse comportamento:

Modo

Comportamento

Quando usar

off_mode

Group commit desativado. INSERT INTO VALUES, Stream Load e HTTP Stream funcionam normalmente.

Quando o group commit não for necessário

sync_mode

Agrupa múltiplas importações em uma transação com base na carga e na propriedade de tabela group_commit_interval. Retorna após o commit da transação. Os dados ficam visíveis imediatamente.

Para escritas de alta concorrência que exigem visibilidade imediata dos dados

async_mode

Grava os dados primeiro nos logs de write-ahead logging (WAL) e retorna imediatamente. Realiza o commit de forma assíncrona com base na carga e no group_commit_interval. Os dados tornam-se visíveis após o commit. Alterna automaticamente para sync_mode ao detectar grandes volumes de dados.

Para escritas de alta frequência onde a baixa latência de escrita é prioritária

Escolha entre modo síncrono e assíncrono:

  • Utilize sync_mode em cenários de alta concorrência onde os dados devem estar visíveis logo após a importação. Esse modo bloqueia a execução até que a transação seja confirmada, garantindo que o resultado retornado confirme a persistência e a disponibilidade imediata para consulta.

  • Prefira async_mode quando a latência de escrita for a maior prioridade. O servidor confirma a importação assim que os dados são gravados no WAL, sem aguardar a conclusão do commit interno. Caso o commit interno falhe, os logs do WAL recuperam os dados. A visibilidade dos dados não é imediata após o retorno da importação.

Crie a tabela de exemplo

Os exemplos neste tópico utilizam a seguinte tabela:

CREATE TABLE `dt` (
    `id` int(11) NOT NULL,
    `name` varchar(50) NULL,
    `score` int(11) NULL
) ENGINE=OLAP
DUPLICATE KEY(`id`)
DISTRIBUTED BY HASH(`id`) BUCKETS 1;

Importe dados usando JDBC

O ApsaraDB for SelectDB suporta o recurso de prepared statement do MySQL via Java Database Connectivity (JDBC). Ao utilizar prepared statements, as instruções SQL e seus planos de importação são armazenados em cache na memória da sessão, reduzindo a sobrecarga de CPU em inserções repetidas.

  1. Adicione a dependência do conector MySQL ao seu projeto:

     <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>5.1.49</version>
     </dependency>
  2. Construa a URL JDBC com prepared statements no lado do servidor ativados:

     jdbc:mysql://selectdb-cn-****.selectdbfe.rds.aliyuncs.com:9030/db?useServerPrepStmts=true
  3. Defina a variável de sessão group_commit. Escolha uma das abordagens:

    • Anexe-a à URL JDBC: `` jdbc:mysql://selectdb-cn-****.selectdbfe.rds.aliyuncs.com:9030/db?useServerPrepStmts=true&sessionVariables=group_commit=async_mode ``

    • Execute uma instrução SQL no momento da conexão: ``java try (Statement statement = conn.createStatement()) { statement.execute("SET group_commit = async_mode;"); } ``

  4. Use um prepared statement para inserir linhas:

     private static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
     private static final String URL_PATTERN = "jdbc:mysql://%s:%d/%s?useServerPrepStmts=true";
     private static final String HOST = "selectdb-cn-****.selectdbfe.rds.aliyuncs.com";
     private static final int PORT = 9030;
     private static final String DB = "db";
     private static final String TBL = "dt";
     private static final String USER = "admin";
     private static final String PASSWD = "***";
     private static final int INSERT_BATCH_SIZE = 10;
    
     public static void main(String[] args) {
         groupCommitInsert();
         //groupCommitInsertBatch
     }
    
     private static void groupCommitInsert() throws Exception {
         Class.forName(JDBC_DRIVER);
         try (Connection conn = DriverManager.getConnection(String.format(URL_PATTERN, HOST, PORT, DB), USER, PASSWD)) {
             // set session variable 'group_commit'
             try (Statement statement = conn.createStatement()) {
                 statement.execute("SET group_commit = async_mode;");
             }
    
             String query = "INSERT INTO " + TBL + " VALUES(?, ?, ?)";
             try (PreparedStatement stmt = conn.prepareStatement(query)) {
                 for (int i = 0; i < INSERT_BATCH_SIZE; i++) {
                     stmt.setInt(1, i);
                     stmt.setString(2, "name" + i);
                     stmt.setInt(3, i + 10);
                     int result = stmt.executeUpdate();
                     System.out.println("rows: " + result);
                 }
             }
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
    
     private static void groupCommitInsertBatch() throws Exception {
         Class.forName(JDBC_DRIVER);
         // add rewriteBatchedStatements=true and cachePrepStmts=true in JDBC url
         // set session variables by sessionVariables=group_commit=async_mode in JDBC url
         try (Connection conn = DriverManager.getConnection(
                 String.format(URL_PATTERN + "&rewriteBatchedStatements=true&cachePrepStmts=true&sessionVariables=group_commit=async_mode", HOST, PORT, DB), USER, PASSWD)) {
    
             String query = "INSERT INTO " + TBL + " VALUES(?, ?, ?)";
             try (PreparedStatement stmt = conn.prepareStatement(query)) {
                 for (int j = 0; j < 5; j++) {
                     // 10 rows per insert
                     for (int i = 0; i < INSERT_BATCH_SIZE; i++) {
                         stmt.setInt(1, i);
                         stmt.setString(2, "name" + i);
                         stmt.setInt(3, i + 10);
                         stmt.addBatch();
                     }
                     int[] result = stmt.executeBatch();
                 }
             }
         } catch (Exception e) {
             e.printStackTrace();
         }
     }

Importe dados usando INSERT INTO

Ative o group commit definindo a variável de sessão group_commit antes de executar instruções INSERT INTO.

Modo assíncrono — os dados são agrupados e confirmados em segundo plano:

-- Enable group commit in async mode. The default value is off_mode.
mysql> SET group_commit = async_mode;

-- The returned label starts with "group_commit", confirming group commit is active.
mysql> INSERT INTO dt VALUES(1, 'Bob', 90), (2, 'Alice', 99);
Query OK, 2 rows affected (0.05 sec)
{'label':'group_commit_a145ce07f1c972fc-bd2c54597052a9ad', 'status':'PREPARE', 'txnId':'181508'}

-- Consecutive inserts sharing the same label and txnId are batched into one import job.
mysql> INSERT INTO dt(id, name) VALUES(3, 'John');
Query OK, 1 row affected (0.01 sec)
{'label':'group_commit_a145ce07f1c972fc-bd2c54597052a9ad', 'status':'PREPARE', 'txnId':'181508'}

-- Data is not visible immediately after the import returns.
mysql> SELECT * FROM dt;
Empty SET (0.01 sec)

-- After ~10 seconds (controlled by group_commit_interval), data becomes visible.
mysql> SELECT * FROM dt;
+------+-------+-------+
| id   | name  | score |
+------+-------+-------+
|    1 | Bob   |    90 |
|    2 | Alice |    99 |
|    3 | John  |  NULL |
+------+-------+-------+
3 rows in set (0.02 sec)

Modo síncrono — retorna apenas após o commit da transação; os dados ficam visíveis imediatamente:

-- Enable group commit in sync mode.
mysql> SET group_commit = sync_mode;

-- The commit interval is controlled by group_commit_interval. The call blocks until the transaction commits.
mysql> INSERT INTO dt VALUES(4, 'Bob', 90), (5, 'Alice', 99);
Query OK, 2 rows affected (10.06 sec)
{'label':'group_commit_d84ab96c09b60587_ec455a33cb0e9e87', 'status':'PREPARE', 'txnId':'3007', 'query_id':'fc6b94085d704a94-a69bfc9a202e66e2'}

-- Data is visible immediately.
mysql> SELECT * FROM dt;
+------+-------+-------+
| id   | name  | score |
+------+-------+-------+
|    1 | Bob   |    90 |
|    2 | Alice |    99 |
|    3 | John  |  NULL |
|    4 | Bob   |    90 |
|    5 | Alice |    99 |
+------+-------+-------+
5 rows in set (0.03 sec)

Desative o group commit:

mysql> SET group_commit = off_mode;

Importe dados usando Stream Load

Para pipelines baseados em logs ou HTTP, passe o cabeçalho group_commit para ativar o group commit nas requisições de Stream Load. Para mais informações sobre Stream Load, consulte Stream Load.

  1. Crie um arquivo chamado data.csv:

     6,Amy,60
     7,Ross,98
  2. Execute a importação com o cabeçalho do modo apropriado: Modo assíncrono:

     # Pass group_commit:async_mode as a request header.
     curl --location-trusted -u {user}:{passwd} -T data.csv \
       -H "group_commit:async_mode" \
       -H "column_separator:," \
       http://{selectdbHost}:{selectdbHttpPort}/api/db/dt/_stream_load

    Resposta esperada:

     {
         "TxnId": 7009,
         "Label": "group_commit_c84d2099208436ab_96e33fda01eddba8",
         "Comment": "",
         "GroupCommit": true,
         "Status": "Success",
         "Message": "OK",
         "NumberTotalRows": 2,
         "NumberLoadedRows": 2,
         "NumberFilteredRows": 0,
         "NumberUnselectedRows": 0,
         "LoadBytes": 19,
         "LoadTimeMs": 35,
         "StreamLoadPutTimeMs": 5,
         "ReadDataTimeMs": 0,
         "WriteDataTimeMs": 26
     }

    Modo síncrono:

     # Pass group_commit:sync_mode as a request header.
     curl --location-trusted -u {user}:{passwd} -T data.csv \
       -H "group_commit:sync_mode" \
       -H "column_separator:," \
       http://{selectdbHost}:{selectdbHttpPort}/api/db/dt/_stream_load

    Resposta esperada:

     {
         "TxnId": 3009,
         "Label": "group_commit_d941bf17f6efcc80_ccf4afdde9881293",
         "Comment": "",
         "GroupCommit": true,
         "Status": "Success",
         "Message": "OK",
         "NumberTotalRows": 2,
         "NumberLoadedRows": 2,
         "NumberFilteredRows": 0,
         "NumberUnselectedRows": 0,
         "LoadBytes": 19,
         "LoadTimeMs": 10044,
         "StreamLoadPutTimeMs": 4,
         "ReadDataTimeMs": 0,
         "WriteDataTimeMs": 10038
     }

    O valor "GroupCommit": true na resposta confirma que o group commit está ativo. O rótulo sempre começa com group_commit.

Configure os limites de commit automático

Ajuste o intervalo de commit ou os limites de tamanho de dados por tabela usando ALTER TABLE.

Intervalo de commit

O intervalo padrão de commit é de 10 segundos.

-- Change the commit interval to 2 seconds.
ALTER TABLE dt SET ("group_commit_interval_ms" = "2000");

Compensações:

Configuração

Vantagens

Desvantagens

Intervalo menor (ex.: 2 segundos)

Menor latência para visibilidade dos dados

Commits mais frequentes, crescimento acelerado de versões e maior pressão de compactação em segundo plano

Intervalo maior (ex.: 30 segundos)

Lotes de commit maiores e menor sobrecarga do sistema

Maior latência para visibilidade dos dados

Defina o intervalo com base na latência tolerável pela sua aplicação entre a escrita e o momento em que os dados se tornam consultáveis. Se o sistema estiver sob alta pressão de compactação, aumente o intervalo.

Limite de tamanho de dados

O limite padrão de tamanho de dados para commit automático é 64 MB.

-- Change the data size threshold to 128 MB.
ALTER TABLE dt SET ("group_commit_data_bytes" = "134217728");

Limitações

Rebaixamento de INSERT INTO VALUES

Quando o group commit está ativado, as seguintes instruções INSERT INTO VALUES são automaticamente rebaixadas para o modo sem group commit:

  • Escritas dentro de uma transação explícita: BEGIN; INSERT INTO VALUES; COMMIT

  • Quando um rótulo é especificado: INSERT INTO dt WITH LABEL {label} VALUES

  • Se VALUES contiver uma expressão: ex.: INSERT INTO dt VALUES (1 + 100)

  • Escritas utilizando atualização de colunas

  • A tabela de destino não suporta alterações leves de schema

Rebaixamento de Stream Load e HTTP Stream

Os seguintes jobs de Stream Load e HTTP Stream são automaticamente rebaixados para o modo sem group commit:

  • Quando um rótulo é especificado usando -H "label:my_label"

  • Uso do modo de commit em duas fases (2PC)

  • Escritas utilizando atualização de colunas

  • A tabela de destino não suporta alterações leves de schema

Modelo Unique Key

O group commit não garante a ordem de commit no modelo Unique Key. Para assegurar a consistência dos dados, utilize o group commit em conjunto com uma coluna de sequência.

Suporte a max_filter_ratio

No modo de importação padrão, filter_ratio determina se o commit deve ocorrer com base na proporção de linhas com falha em relação ao total. No modo group commit, importações de múltiplos clientes são mescladas em uma única importação interna e confirmadas como uma unidade.

O group commit suporta parcialmente a semântica de max_filter_ratio: essa semântica entra em vigor apenas quando o número total de linhas importadas não excede o valor do item de configuração do backend (BE) group_commit_memory_rows_for_max_filter_ratio. O valor padrão é 10000.

Comportamento do WAL no modo assíncrono

No async_mode, cada importação é primeiramente gravada nos logs do WAL:

  • Se o commit interno for bem-sucedido, os logs do WAL são excluídos imediatamente.

  • Se o commit interno falhar, os logs do WAL são usados para recuperar os dados.

O sistema alterna automaticamente de async_mode para sync_mode nas situações abaixo para proteger o espaço em disco:

  • Os dados importados ocupam mais de 80% de um único diretório WAL.

  • Um job de Stream Load fragmentado é enviado com tamanho total de dados desconhecido.

  • O volume de dados é pequeno, mas o espaço disponível em disco é insuficiente.

Alterações de schema

Se uma alteração pesada de schema estiver em sua fase final de modificação de metadados, o sistema rejeita novos group commits para garantir que os logs do WAL permaneçam compatíveis com o schema da tabela. Os clientes afetados recebem a exceção:

insert table ${table_name} is blocked on schema change

Tente novamente a importação no cliente quando essa exceção ocorrer.

Alterações leves de schema (adicionar ou excluir colunas, alterar comprimento de VARCHAR, renomear colunas) não bloqueiam group commits. Todas as outras alterações de schema são consideradas pesadas.