Binlog de Hologres registra las operaciones INSERT, UPDATE, DELETE y TRUNCATE como un flujo de eventos de cambio. Esta página explica cómo consumir dicho flujo mediante Java Database Connectivity (JDBC) directamente o a través de Holo-Client, el SDK de Java para Hologres.
Requisitos previos
Antes de empezar, asegúrese de cumplir con lo siguiente:
Binlog habilitado en la tabla destino. Consulte Subscribe to Hologres Binlog data.
-
La extensión
hg_binlogcreada (solo para Hologres V1.x). A partir de la versión V2.0, la extensión viene integrada y no requiere configuración manual. En versiones anteriores a V2.0, un superusuario debe ejecutar la siguiente sentencia una vez por base de datos. Si crea una nueva base de datos, ejecútela nuevamente.ImportanteNunca utilice
DROP EXTENSION <extension_name> CASCADE. La opciónCASCADEelimina todos los datos de la extensión, incluidos PostGIS, RoaringBitmap, Proxima, Binlog y BSI, además de borrar todos los objetos dependientes como tablas, vistas, metadatos y datos del servidor.-- Create the extension. CREATE EXTENSION hg_binlog; -- Remove the extension. DROP EXTENSION hg_binlog; -
Acceso configurado según su versión de Hologres: para crear una publicación y un slot de replicación (obligatorio en todas las versiones que usan este método), el usuario debe contar con uno de los siguientes conjuntos de permisos:
Permisos de superusuario en la instancia
Permisos de propietario en la tabla destino, permisos
CREATE DATABASEy el rol de replicación (Replication Role) en la instancia
NotaA partir de la versión V2.0, el modo Holohub ya no es compatible. Antes de actualizar a V2.0 o superior, actualice primero Flink a la versión 8.0.5. Después de la actualización, se utiliza automáticamente el modo JDBC.
Versión de Hologres
Versión del motor de Flink
Requisitos
V2.1 y posteriores
8.0.5 y posteriores
Permisos de lectura en la tabla destino. No se requiere slot de replicación.
V2.0
8.0.5 y anteriores
Una publicación y un slot de replicación. Consulte Create a publication and a replication slot.
V1.3 y anteriores
8.0.5 y anteriores
Permisos de lectura en la tabla destino. Se utiliza automáticamente el modo Holohub.
Limitaciones
El consumo de Binlog basado en JDBC requiere Hologres V1.1 o posterior.
-
Tipos de datos de columna compatibles: INTEGER, BIGINT, SMALLINT, TEXT, CHAR(n), VARCHAR(n), REAL, DOUBLE PRECISION, BOOLEAN, NUMERIC(38.8), DATE, TIME, TIMETZ, TIMESTAMP, TIMESTAMPTZ, BYTEA, JSON, SERIAL, OID, int4[], int8[], float4[], float8[], boolean[] y text[]. Si una tabla contiene columnas de tipos no compatibles, el consumo fallará. A partir de la versión V1.3.36, también se admiten columnas JSONB. Habilite el siguiente parámetro de Grand Unified Configuration (GUC) antes de iniciar el consumo:
-- Enable at the session level. SET hg_experimental_enable_binlog_jsonb = ON; -- Enable at the database level. ALTER DATABASE <db_name> SET hg_experimental_enable_binlog_jsonb = ON; Cada shard de cada tabla consumida utiliza una conexión Walsender. Las conexiones Walsender son independientes de las conexiones regulares.
-
El número máximo de Walsenders por nodo frontend varía según la versión. Para comprobar el límite actual, ejecute:
Identifique los trabajos activos de consumo de Binlog por JDBC y detenga aquellos que no sean necesarios.
Revise si la configuración del grupo de tablas y el recuento de shards son adecuados. Consulte Best practices for setting table groups.
Si aun así se supera el límite, escale horizontalmente la instancia.
Versión
Máximo de Walsenders por nodo frontend
V2.2 y posteriores
600
V2.0 y V2.1
1,000
V1.1.26 a V2.0
100
SHOW max_wal_senders;Valores predeterminados: El límite total es
max_wal_senders × número de nodos frontend. Para conocer el número de nodos frontend correspondiente a una especificación de instancia determinada, consulte Instance management. Utilice esta fórmula para estimar cuántas tablas puede consumir simultáneamente:Number of tables ≤ (max_wal_senders × number of frontend nodes) / table shard countEjemplo: Una instancia con dos nodos frontend que ejecuta V2.2 tiene un total de 600 × 2 = 1,200 Walsenders. Para tablas con un recuento de shards de 20, puede soportar hasta 60 consumos concurrentes de tablas. Si dos trabajos consumen de la misma tabla simultáneamente, cada uno utiliza 20 Walsenders, lo que suma 40 al total. Si se alcanza el límite de Walsenders, verá el siguiente mensaje:
FATAL: sorry, too many wal senders alreadyPara resolver esto:
Instancias secundarias de solo lectura: El consumo de Binlog mediante JDBC no es compatible antes de la versión V2.0.18. A partir de V2.0.18, las instancias secundarias admiten el consumo de Binlog, pero no registran el progreso del consumo.
Creación de una publicación y un slot de replicación
Obligatorio para Hologres V2.0 y anteriores. A partir de V2.1, los usuarios con solo permisos de lectura en la tabla destino pueden consumir datos de Binlog sin necesidad de un slot de replicación; omita esta sección si utiliza V2.1 o posterior.
Publicación
Una publicación define qué cambios de tabla están disponibles para la replicación lógica. En Hologres, una publicación se vincula exactamente a una tabla física, y dicha tabla debe tener Binlog habilitado.
Crear una publicación
CREATE PUBLICATION <name> FOR TABLE <table_name>;
|
Parámetro |
Descripción |
|
|
Nombre personalizado para la publicación. |
|
|
Nombre de la tabla destino. |
Ejemplo:
CREATE PUBLICATION hg_publication_test_1 FOR TABLE test_message_src;
Consultar publicaciones
SELECT * FROM pg_publication;
Salida de ejemplo:
pubname | pubowner | puballtables | pubinsert | pubupdate | pubdelete | pubtruncate
------------------------+----------+--------------+-----------+-----------+-----------+-------------
hg_publication_test_1 | 16728 | f | t | t | t | t
(1 row)
|
Campo |
Descripción |
|
|
Nombre de la publicación. |
|
|
Propietario de la publicación. |
|
|
Indica si se incluyen todas las tablas. Siempre es |
|
|
Indica si se publican los eventos INSERT. Valor predeterminado: |
|
|
Indica si se publican los eventos UPDATE. Valor predeterminado: |
|
|
Indica si se publican los eventos DELETE. Valor predeterminado: |
|
|
Indica si se publican los eventos TRUNCATE. Valor predeterminado: |
Para obtener más información sobre los tipos de eventos de Binlog, consulte Binlog format and principles.
Consultar tablas en una publicación
SELECT * FROM pg_publication_tables;
Salida de ejemplo:
pubname | schemaname | tablename
------------------------+------------+------------------
hg_publication_test_1 | public | test_message_src
(1 row)
Eliminar una publicación
DROP PUBLICATION <name>;
Ejemplo:
DROP PUBLICATION hg_publication_test_1;
Slot de replicación
Un slot de replicación inactivo continúa reteniendo datos WAL en el servidor. Si un slot no se consume, los datos WAL se acumulan y pueden agotar el almacenamiento. Elimine cualquier slot que ya no necesite.
Un slot de replicación rastrea el progreso del consumo de una publicación y permite reanudar la transmisión. Tras un failover, un consumidor puede recuperarse desde el último punto de control confirmado registrado en el slot.
Solo los superusuarios y los usuarios con el rol de replicación (Replication Role) pueden crear y utilizar slots de replicación. Para otorgar o revocar este rol:
-- Grant the Replication Role to a user.
ALTER ROLE <user_name> REPLICATION;
-- Revoke the Replication Role from a user.
ALTER ROLE <user_name> NOREPLICATION;
user_name es un ID de cuenta de Alibaba Cloud o un usuario de Resource Access Management (RAM). Consulte Account overview.
Crear un slot de replicación
CALL hg_create_logical_replication_slot('<replication_slot_name>', 'hgoutput', '<publication_name>');
|
Parámetro |
Descripción |
|
|
Nombre personalizado para el slot de replicación. |
|
|
Plugin de salida para el formato Binlog. Solo se admite |
|
|
Nombre de la publicación a la que se vinculará. |
Ejemplo:
CALL hg_create_logical_replication_slot('hg_replication_slot_1', 'hgoutput', 'hg_publication_test_1');
Consultar slots de replicación
SELECT * FROM hologres.hg_replication_slot_properties;
Salida de ejemplo:
slot_name | property_key | property_value
------------------------+--------------+------------------------
hg_replication_slot_1 | plugin | hgoutput
hg_replication_slot_1 | publication | hg_publication_test_1
hg_replication_slot_1 | parallelism | 1
(3 rows)
|
Campo |
Descripción |
|
|
Nombre del slot de replicación. |
|
|
Uno de los siguientes: |
|
|
Valor de la propiedad correspondiente. |
Consultar el paralelismo requerido
Dado que Hologres es una base de datos distribuida, los datos de cada tabla se distribuyen en varios shards. Se requiere una conexión por shard para consumir la tabla completa. Para comprobar cuántas conexiones concurrentes requiere hg_replication_slot_1:
SELECT hg_get_logical_replication_slot_parallelism('hg_replication_slot_1');
Salida de ejemplo:
hg_get_logical_replication_slot_parallelism
---------------------------------------------
20
Consultar el progreso del consumo
La tabla hologres.hg_replication_progress registra el desplazamiento del consumidor que usted confirma explícitamente.
SELECT * FROM hologres.hg_replication_progress;
Salida de ejemplo:
slot_name | parallel_index | lsn
------------------------+----------------+-----
hg_replication_slot_1 | 0 | 66
hg_replication_slot_1 | 1 | 122
hg_replication_slot_1 | 2 | 119
(3 rows)
|
Campo |
Descripción |
|
|
Nombre del slot de replicación. |
|
|
Índice de la conexión concurrente (una por shard). |
|
|
Número de secuencia de registro (LSN) del último registro de Binlog consumido que se confirmó. |
La tabla
hologres.hg_replication_progressse crea únicamente después del primer consumo de Binlog.La tabla solo registra los desplazamientos que usted confirma explícitamente al llamar a la función de confirmación de LSN en su código. Es posible que el valor registrado no coincida con la posición real del consumidor. Rastree el LSN en el lado del cliente y utilícelo como punto de recuperación.
Las confirmaciones de puntos de control solo son efectivas cuando se consume mediante un slot de replicación. Al consumir solo por nombre de tabla (sin
withSlotName), no se registra el progreso.
Eliminar un slot de replicación
Elimine un slot cuando ya no lo necesite para evitar la acumulación de WAL. Los slots no se cierran automáticamente cuando se detiene un trabajo de consumo; debe eliminarlos manualmente.
CALL hg_drop_logical_replication_slot('<replication_slot_name>');
Ejemplo:
CALL hg_drop_logical_replication_slot('hg_replication_slot_1');
Consumo de datos de Binlog mediante JDBC
El consumo basado en JDBC le ofrece un control detallado sobre cada shard. Debe crear un PGReplicationStream por shard y gestionar el paralelismo por su cuenta.
Paso 1: Agregar dependencias
Utilice JDBC 42.2.18 o posterior. Agregue las siguientes dependencias a su archivo pom.xml:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.3.8</version>
</dependency>
<!-- Used to get the table schema and decode Binlog records -->
<dependency>
<groupId>com.alibaba.hologres</groupId>
<artifactId>holo-client</artifactId>
<version>2.2.10</version>
</dependency>
Paso 2: Conectar y consumir
El siguiente ejemplo se conecta a un slot de replicación, lee registros de Binlog de un shard e imprime cada registro.
import com.alibaba.hologres.client.HoloClient;
import com.alibaba.hologres.client.HoloConfig;
import com.alibaba.hologres.client.impl.binlog.HoloBinlogDecoder;
import com.alibaba.hologres.client.model.Record;
import com.alibaba.hologres.client.model.TableSchema;
import org.postgresql.PGConnection;
import org.postgresql.PGProperty;
import org.postgresql.replication.LogSequenceNumber;
import org.postgresql.replication.PGReplicationStream;
import java.nio.ByteBuffer;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
public class Test {
public static void main(String[] args) throws Exception {
String username = "";
String password = "";
String url = "jdbc:postgresql://Endpoint:Port/db_test";
// Set up the JDBC connection for replication.
Properties properties = new Properties();
PGProperty.USER.set(properties, username);
PGProperty.PASSWORD.set(properties, password);
PGProperty.ASSUME_MIN_SERVER_VERSION.set(properties, "9.4");
// Required for Binlog consumption.
PGProperty.REPLICATION.set(properties, "database");
try (Connection connection = DriverManager.getConnection(url, properties)) {
// Each PGReplicationStream consumes one shard. Set shardId for each stream.
int shardId = 0;
PGConnection pgConnection = connection.unwrap(PGConnection.class);
PGReplicationStream pgReplicationStream = pgConnection.getReplicationAPI()
.replicationStream()
.logical()
// V2.1+: two options
// Option 1: specify the replication slot name. table_name is ignored.
// Option 2: omit withSlotName and set table_name in withSlotOption instead.
.withSlotName("slot_name")
.withSlotOption("table_name", "public.test_message_src")
.withSlotOption("parallel_index", shardId)
.withSlotOption("batch_size", "1024")
.withSlotOption("start_time", "2021-01-01 00:00:00")
.withSlotOption("start_lsn", "0")
.start();
// HoloClient is needed to get the table schema for decoding.
HoloConfig holoConfig = new HoloConfig();
holoConfig.setJdbcUrl(url);
holoConfig.setUsername(username);
holoConfig.setPassword(password);
HoloClient client = new HoloClient(holoConfig);
// Decode raw binary Binlog data into BinlogRecord objects.
TableSchema schema = client.getTableSchema("test_message_src", true);
HoloBinlogDecoder decoder = new HoloBinlogDecoder(schema);
// Track the last consumed LSN for checkpoint recovery.
Long currentLsn = 0L;
ByteBuffer byteBuffer = pgReplicationStream.readPending();
while (true) {
if (byteBuffer != null) {
List<BinlogRecord> records = decoder.decode(shardId, byteBuffer);
Long latestLsn = 0L;
for (BinlogRecord record : records) {
latestLsn = record.getBinlogLsn();
// Process the record here.
System.out.println("lsn: " + latestLsn + ", record: " + Arrays.toString(record.getValues()));
}
// Save the latest LSN as the recovery point.
currentLsn = latestLsn;
pgReplicationStream.forceUpdateStatus();
}
byteBuffer = pgReplicationStream.readPending();
}
}
}
}
Parámetros
Comportamiento de withSlotName según la versión:
|
Versión |
Comportamiento |
|
Anterior a V2.1 |
Especifique el nombre de un slot de replicación existente. Obligatorio. |
|
V2.1 y posteriores |
Opcional. Omita |
Parámetros de withSlotOption:
|
Parámetro |
Obligatorio |
Descripción |
|
|
Sí, cuando no se establece |
Tabla destino a consumir. Formato: |
|
|
Sí |
Índice del shard a consumir. Un |
|
|
No |
Inicia el consumo desde esta marca de tiempo. Ejemplo: |
|
|
No |
Inicia el consumo desde el registro posterior a este LSN. Tiene prioridad sobre |
|
|
No |
Máximo de registros por recuperación. Valor predeterminado: 1,024. |
Comportamiento de inicio predeterminado (cuando no se establece ni start_lsn ni start_time):
|
Escenario |
Comportamiento |
|
Primer consumo desde un slot de replicación |
Comienza desde el inicio, similar al desplazamiento más temprano (earliest offset) de Kafka. |
|
Consumos subsiguientes desde un slot de replicación |
Se reanuda desde el último punto de control confirmado. |
|
Consumo solo por nombre de tabla (sin |
Siempre comienza desde el inicio. |
Campos de BinlogRecord
BinlogRecord expone los siguientes campos del sistema de Binlog:
|
Método |
Retorna |
|
|
El LSN del registro de Binlog. |
|
|
La marca de tiempo del sistema cuando ocurrió el evento. |
|
|
El tipo de evento: INSERT, UPDATE, DELETE o TRUNCATE. |
Para consultar la referencia completa de campos, véase Subscribe to Hologres Binlog data.
Confirmar el punto de control
Después de consumir datos de Binlog, confirme el punto de control llamando a la función de confirmación de LSN. Esto cumple dos propósitos: permite que el servidor sepa qué segmentos WAL son seguros para archivar o descartar, y proporciona un punto de recuperación para que el consumo pueda reanudarse desde la posición correcta tras un failover.
El código de ejemplo anterior no incluye este paso; agréguelo según los requisitos de recuperación de su aplicación.
Consumo de datos de Binlog mediante Holo-Client
Holo-Client simplifica el consumo de Binlog al gestionar automáticamente las conexiones a nivel de shard. Especifique la tabla destino y Holo-Client se encargará del resto.
El número de conexiones es igual al recuento de shards de la tabla.
Guarde el punto de control por shard para que el consumo pueda reanudarse desde la posición correcta tras un fallo de red u otra interrupción.
Utilice Holo-Client 2.2.10 o posterior. Las versiones 2.2.9 y anteriores presentan una fuga de memoria.
Paso 1: Agregar dependencias
<dependency>
<groupId>com.alibaba.hologres</groupId>
<artifactId>holo-client</artifactId>
<version>2.2.10</version>
</dependency>
Paso 2: Conectar y consumir
El siguiente ejemplo se suscribe a una tabla, procesa cada registro, guarda un punto de control por shard y reintenta en caso de error.
import com.alibaba.hologres.client.BinlogShardGroupReader;
import com.alibaba.hologres.client.Command;
import com.alibaba.hologres.client.HoloClient;
import com.alibaba.hologres.client.HoloConfig;
import com.alibaba.hologres.client.Subscribe;
import com.alibaba.hologres.client.exception.HoloClientException;
import com.alibaba.hologres.client.impl.binlog.BinlogOffset;
import com.alibaba.hologres.client.model.binlog.BinlogHeartBeatRecord;
import com.alibaba.hologres.client.model.binlog.BinlogRecord;
import java.util.HashMap;
import java.util.Map;
public class HoloBinlogExample {
public static BinlogShardGroupReader reader;
public static void main(String[] args) throws Exception {
String username = "";
String password = "";
String url = "jdbc:postgresql://ip:port/database";
String tableName = "test_message_src";
String slotName = "hg_replication_slot_1";
HoloConfig holoConfig = new HoloConfig();
holoConfig.setJdbcUrl(url);
holoConfig.setUsername(username);
holoConfig.setPassword(password);
holoConfig.setBinlogReadBatchSize(128);
holoConfig.setBinlogIgnoreDelete(true);
holoConfig.setBinlogIgnoreBeforeUpdate(true);
holoConfig.setBinlogHeartBeatIntervalMs(5000L);
HoloClient client = new HoloClient(holoConfig);
// Get the shard count to initialize per-shard checkpoint tracking.
int shardCount = Command.getShardCount(client, client.getTableSchema(tableName));
// Initialize the checkpoint map with LSN 0 for each shard.
Map<Integer, Long> shardIdToLsn = new HashMap<>(shardCount);
for (int i = 0; i < shardCount; i++) {
shardIdToLsn.put(i, 0L);
}
// Before V2.1: tableName and slotName are both required.
// V2.1 and later: tableName is enough (slotName defaults to "hg_table_name_slot").
Subscribe subscribe = Subscribe.newStartTimeBuilder(tableName, slotName)
.setBinlogReadStartTime("2021-01-01 12:00:00")
.build();
reader = client.binlogSubscribe(subscribe);
BinlogRecord record;
int retryCount = 0;
while (true) {
try {
if (reader.isCanceled()) {
// Re-create the reader using the last saved checkpoint.
reader = client.binlogSubscribe(subscribe);
}
while ((record = reader.getBinlogRecord()) != null) {
if (record instanceof BinlogHeartBeatRecord) {
// All data up to this heartbeat's timestamp has been consumed on this shard.
continue;
}
// Process the record here.
System.out.println(record);
// Save the checkpoint so consumption can resume from here on failure.
shardIdToLsn.put(record.getShardId(), record.getBinlogLsn());
retryCount = 0;
}
} catch (HoloClientException e) {
if (++retryCount > 10) {
throw new RuntimeException(e);
}
System.out.println(String.format(
"Binlog read failed: %s. Retrying (%d/10)...", e.getMessage(), retryCount));
// Wait before retrying, with increasing backoff.
Thread.sleep(5000L * retryCount);
// Resume from the saved per-shard checkpoint.
Subscribe.OffsetBuilder subscribeBuilder = Subscribe.newOffsetBuilder(tableName, slotName);
for (int i = 0; i < shardCount; i++) {
subscribeBuilder.addShardStartOffset(i,
new BinlogOffset().setSequence(shardIdToLsn.get(i)));
}
subscribe = subscribeBuilder.build();
reader.cancel();
}
}
}
}
Parámetros
Tipos de Subscribe:
|
Tipo |
Cuándo utilizarlo |
|
|
Para iniciar el consumo desde una marca de tiempo específica. |
|
|
Para iniciar el consumo desde un LSN específico por shard; utilícelo para la recuperación basada en puntos de control. |
Nota sobre versiones: Antes de V2.1, tanto tableName como slotName son obligatorios. A partir de V2.1, solo se requiere tableName (equivalente a usar el nombre de slot fijo hg_table_name_slot).
Parámetros de Binlog en HoloConfig:
|
Parámetro |
Valor predeterminado |
Descripción |
|
|
1,024 |
Máximo de registros por recuperación por shard. |
|
|
-1 (deshabilitado) |
Intervalo en milisegundos entre mensajes |
|
|
|
Omite los eventos DELETE. |
|
|
|
Omite los registros de imagen previa (before-image) para eventos UPDATE. |
Solución de problemas
La tabla hologres.hg_replication_progress falta o está vacía
Si la tabla no existe o no muestra datos después de confirmar el progreso del consumo, compruebe lo siguiente:
Consumo por nombre de tabla sin slot de replicación. Si no estableció withSlotName (JDBC) o no especificó un slot (Holo-Client), el seguimiento de progreso no es compatible. La tabla no se creará ni se actualizará. Cambie al consumo basado en slots de replicación para habilitar el seguimiento de progreso.
Primer consumo en una instancia secundaria de solo lectura. En instancias secundarias que ejecutan versiones de Hologres anteriores a V2.0.18, la tabla hologres.hg_replication_progress no puede crearse durante el primer consumo de Binlog. Consuma desde la instancia principal una vez y luego vuelva a la instancia secundaria.
Si ninguna de estas situaciones aplica, contacte al soporte técnico a través del grupo de DingTalk de Hologres. Consulte How do I get more online support?.
Pasos siguientes
Subscribe to Hologres Binlog data — Formato de Binlog, tipos de eventos y campos del sistema
Best practices for setting table groups — Optimización del recuento de shards para controlar el uso de Walsender
Instance management — Recuentos de nodos frontend por especificación de instancia