La extensión rds_duckdb para ApsaraDB RDS for PostgreSQL reenvía automáticamente las consultas SELECT analíticas al motor orientado a columnas DuckDB para su ejecución. Esto acelera significativamente las consultas complejas sin necesidad de modificar sus sentencias SQL. Este tema describe cómo reenviar consultas a DuckDB después de crear la extensión y las tablas de DuckDB, y abarca funciones avanzadas relacionadas y métodos de solución de problemas.
Para preguntas, discusiones o comentarios sobre esta extensión, únase al grupo de DingTalk de extensiones de ApsaraDB RDS for PostgreSQL (ID: 103525002795).
Requisitos previos
Antes de utilizar esta función, asegúrese de cumplir con las siguientes condiciones:
La instancia primaria ejecuta ApsaraDB RDS for PostgreSQL 13 a 18 con una versión menor del motor 20260130 o posterior. Para acelerar consultas en instancias de solo lectura, estas deben ejecutar PostgreSQL 16 a 18 con una versión menor del motor 20260130 o posterior.
Para utilizar la sincronización de tablas particionadas o la creación automática de tablas de DuckDB, la versión menor del motor debe ser 20260330 o posterior.
La extensión rds_duckdb está creada.
Notas de uso
rds_duckdb acelera únicamente consultas SELECT de solo lectura. Las sentencias DML (INSERT, UPDATE y DELETE), las sentencias DDL y las consultas que involucran tablas sin una tabla de DuckDB correspondiente se derivan a PostgreSQL de forma predeterminada.
Los hints admiten únicamente el parámetro
rds_duckdb.executiony ningún otro parámetro.Si se conecta a la instancia mediante DMS, utilice un hint para habilitar la aceleración, ya que DMS reescribe las sentencias SQL.
Las consultas simples, como las consultas puntuales y los escaneos de rango pequeño, pueden ejecutarse más lento en DuckDB debido a la sobrecarga de reenvío e inicio. Configure
rds_duckdb.plan_cost_thresholdpara filtrar las consultas de bajo costo. Para obtener más información, consulte Umbral de costo de ejecución (plan_cost_threshold).
Procedimiento
La aceleración de consultas consta de tres pasos: habilitar la aceleración, verificar el reenvío de consultas y consultar los registros de ejecución.
Paso 1: Habilitar la aceleración de DuckDB
Utilice uno de los siguientes métodos para reenviar consultas SELECT a DuckDB:
-
Método 1: Usar un hint (nivel de sentencia)
Agregue un hint antes de la sentencia SELECT. El hint surte efecto solo en dicha sentencia, lo cual es adecuado para verificaciones temporales o para acelerar una única consulta lenta:
/*+ set(rds_duckdb.execution on) */ SELECT * FROM my_table WHERE id = 1; -
Método 2: Configurar un parámetro a nivel de sesión
Ejecute el siguiente comando en la sesión actual. Todas las consultas elegibles en la sesión se envían a DuckDB:
SET rds_duckdb.execution = on;
Paso 2: Verificar el reenvío de consultas (EXPLAIN / EXPLAIN ANALYZE)
Revise el plan de ejecución para determinar si una sentencia SQL se reenvía a DuckDB.
-
Ejemplo 1: Una consulta de tabla única se reenvía a DuckDB
/*+ set(rds_duckdb.execution on) */ EXPLAIN SELECT * FROM test_hint;El plan esperado contiene
Custom Scan (DuckDBScan)yDuckDB Execution Plan:QUERY PLAN ------------------------------------------------------------ Custom Scan (DuckDBScan) (cost=0.00..0.00 rows=0 width=0) DuckDB Execution Plan: ┌───────────────────────────┐ │ SEQ_SCAN │ │ ──────────────────── │ │ Table: test_hint │ │ Type: Sequential Scan │ │ Projections: a │ │ │ │ ~0 Rows │ └───────────────────────────┘ -
Ejemplo 2: El plan nativo de PostgreSQL se restaura tras deshabilitar el reenvío
/*+ set(rds_duckdb.execution off) */ EXPLAIN SELECT * FROM test_hint;Plan esperado:
QUERY PLAN ----------------------- Seq Scan on test_hint (1 row)
Paso 3: Consultar los registros de DuckDB (opcional)
El parámetro rds_duckdb.enable_log_warning controla si los mensajes WARNING se devuelven al cliente. Esto ayuda a identificar por qué una consulta no es ejecutada por DuckDB.
-
Habilite la salida de WARNING (surte efecto inmediatamente a nivel de sesión):
SET rds_duckdb.enable_log_warning = on; -
Compruebe la salida del cliente. Cuando
enable_log_warning = on, los mensajes WARNING se devuelven en los siguientes escenarios:La sentencia SQL se deriva a PostgreSQL:
Fallback postgres due to ...No se admiten operaciones de escritura:
Modification operations on DuckDB tables are currently not supported, fallback to PG.La sentencia no necesita ser ejecutada por DuckDB:
Statements don't need to be handed over to DuckDB, fallback to PG.
Cuando enable_log_warning = off (predeterminado), los mensajes anteriores se escriben en los registros con nivel DEBUG1 y no se devuelven al cliente.
Funciones avanzadas
Las siguientes funciones ayudan a optimizar el rendimiento de la aceleración o a ampliar el alcance de la sincronización. Habilite estas funciones según sus requisitos empresariales.
Sincronización automática de DDL
Los cambios de esquema (DDL) en ApsaraDB RDS for PostgreSQL pueden sincronizarse automáticamente con DuckDB. Los siguientes parámetros controlan la sincronización:
|
Parámetro |
Descripción |
Valor predeterminado |
|
|
Especifica si se habilita la sincronización de DDL. |
|
|
|
La acción a tomar ante un fallo de sincronización: |
|
Operaciones DDL compatibles verificadas:
-- Add a column
ALTER TABLE tbl ADD COLUMN extra_1 int;
-- Drop a column
ALTER TABLE tbl DROP COLUMN extra_1;
-- Add a column with a default value
ALTER TABLE tbl ADD COLUMN extra_1 int DEFAULT 0;
-- Drop the default value
ALTER TABLE tbl ALTER COLUMN extra_1 DROP DEFAULT;
-- Change the column type
ALTER TABLE tbl ALTER COLUMN extra_1 TYPE varchar;
-- Rename a column
ALTER TABLE tbl RENAME COLUMN extra_1 TO extra_2;
-- Rename a table
ALTER TABLE tbl RENAME TO tbl_new;
-- DDL within a transaction
BEGIN;
ALTER TABLE tbl ADD COLUMN extra_3 int;
INSERT INTO tbl VALUES (..., 1);
COMMIT;
-- Roll back DDL by using a savepoint
BEGIN;
SAVEPOINT s1;
ALTER TABLE tbl DROP COLUMN extra_1;
ROLLBACK TO SAVEPOINT s1;
COMMIT;
Ejemplos de operaciones DDL no compatibles que activan la derivación:
-- Change the schema of a table (triggers an automatic full refresh or a conflict based on the fail_action setting)
ALTER TABLE tbl SET SCHEMA nsp1;
-- Drop the primary key (interrupts synchronization, and queries fall back to PostgreSQL)
ALTER TABLE tbl DROP CONSTRAINT tbl_pkey;
Después de eliminar la clave primaria, la tabla entra en estado not syncing en duckdb_sync_stat y las consultas dejan de ser ejecutadas por DuckDB hasta que se restaure la clave primaria o REPLICA IDENTITY y se actualice manualmente la tabla.
Sincronización de tablas particionadas
Esta función solo es compatible con instancias que ejecutan la versión menor del motor 20260330 o posterior.
rds_duckdb puede sincronizar tablas particionadas de PostgreSQL (de un solo nivel y multinivel) con DuckDB.
Para sincronizar una tabla particionada de PostgreSQL, debe crear tablas de DuckDB para la partición raíz y todas las particiones hoja. El siguiente ejemplo muestra una tabla particionada de un solo nivel:
-- Create a partitioned table in PostgreSQL
CREATE TABLE test_partition (
id int,
age int,
primary key (id, age)
) PARTITION BY RANGE (age);
CREATE TABLE test_partition_a PARTITION OF test_partition FOR VALUES FROM (0) TO (18);
CREATE TABLE test_partition_b PARTITION OF test_partition FOR VALUES FROM (18) TO (30);
CREATE TABLE test_partition_c PARTITION OF test_partition FOR VALUES FROM (30) TO (60);
INSERT INTO test_partition SELECT i, i FROM generate_series(0, 59) i;
-- Synchronize the root partition and all leaf partitions to DuckDB
SELECT rds_duckdb.create_duckdb_tables('{test_partition, test_partition_a, test_partition_b, test_partition_c}');
Ejemplo de una consulta en la partición raíz:
/*+ set(rds_duckdb.execution on) */ EXPLAIN SELECT * FROM test_partition WHERE age >= 10 AND age < 20;
Plan esperado (solo se escanean las particiones afectadas después de la poda):
Custom Scan (DuckDBScan) (cost=0.00..0.00 rows=0 width=0)
DuckDB Execution Plan:
┌───────────────────────────┐
│ UNION ├──────────────┐
└─────────────┬─────────────┘ │
┌─────────────┴─────────────┐┌─────────────┴─────────────┐
│ SEQ_SCAN ││ SEQ_SCAN │
│ Table: ││ Table: │
│ test_partition_a ││ test_partition_b │
│ Type: Sequential Scan ││ Type: Sequential Scan │
│ Projections: ││ Projections: │
│ Filters: ││ Filters: │
│ age>=10 AND age<20││ age>=10 AND age<20│
└───────────────────────────┘└───────────────────────────┘
También se admiten tablas particionadas multinivel. Ejemplo:
CREATE TABLE test_multi_partition (
id serial,
sale_id int NOT NULL,
sale_date date NOT NULL,
amount numeric(15,2) NOT NULL,
primary key(sale_id, sale_date)
) PARTITION BY RANGE (sale_date);
CREATE TABLE test_multi_partition_a_l1 PARTITION OF test_multi_partition
FOR VALUES FROM ('2024-1-1') TO ('2025-1-1') PARTITION BY RANGE (sale_date);
CREATE TABLE test_multi_partition_b_l1 PARTITION OF test_multi_partition
FOR VALUES FROM ('2025-1-1') TO ('2026-1-1') PARTITION BY RANGE (sale_date);
CREATE TABLE test_multi_partition_a_l2_1 PARTITION OF test_multi_partition_a_l1
FOR VALUES FROM ('2024-1-1') TO ('2024-7-1');
CREATE TABLE test_multi_partition_a_l2_2 PARTITION OF test_multi_partition_a_l1
FOR VALUES FROM ('2024-7-1') TO ('2025-1-1');
CREATE TABLE test_multi_partition_b_l2_1 PARTITION OF test_multi_partition_b_l1
FOR VALUES FROM ('2025-1-1') TO ('2025-7-1');
CREATE TABLE test_multi_partition_b_l2_2 PARTITION OF test_multi_partition_b_l1
FOR VALUES FROM ('2025-7-1') TO ('2026-1-1');
INSERT INTO test_multi_partition (sale_id, sale_date, amount)
SELECT (random() * 100)::int, '2024-01-1'::date + i, (random() * 1000)::numeric(15,2)
FROM generate_series(1, 730) i;
-- Synchronize the partitions at all levels
SELECT rds_duckdb.create_duckdb_tables('{
test_multi_partition,
test_multi_partition_a_l1, test_multi_partition_b_l1,
test_multi_partition_a_l2_1, test_multi_partition_a_l2_2,
test_multi_partition_b_l2_1, test_multi_partition_b_l2_2
}');
Si las particiones hoja no están sincronizadas, una consulta en la partición raíz podría activar la derivación debido a la falta de algunas tablas.
Los cambios DDL en tablas particionadas, como ATTACH PARTITION y DETACH PARTITION, también son controlados por enable_ddl_replication. El comportamiento ante un fallo lo determina ddl_replication_fail_action.
Creación automática de tablas de DuckDB
Esta función solo es compatible con instancias que ejecutan la versión menor del motor 20260330 o posterior.
Además de llamar manualmente a create_duckdb_table(), puede usar rds_duckdb para crear automáticamente tablas de DuckDB. Una vez habilitada la creación automática, cuando ejecute CREATE TABLE en PostgreSQL y se cumplan las condiciones de sincronización, el sistema creará automáticamente la tabla de DuckDB correspondiente e iniciará la sincronización incremental.
-- Enable automatic creation (USERSET level; can be controlled at the session level)
SET rds_duckdb.auto_create_duckdb_table = on;
-- Create a table in PostgreSQL, and the DuckDB table is automatically created
CREATE TABLE auto_tbl(id int primary key, val text);
INSERT INTO auto_tbl VALUES (1, 'hello');
-- Query DuckDB data directly
/*+ set(rds_duckdb.execution on) */ SELECT * FROM auto_tbl;
Resultado esperado (cuando la sincronización es normal):
sync_table | sync_status_description | sync_error_description
-----------------+-------------------------+------------------------
public.auto_tbl | data syncing | no errors
Notas sobre el comportamiento:
Esta función aplica únicamente a tablas regulares. La creación automática para tablas particionadas no es compatible en la versión actual.
La tabla debe tener una clave primaria o REPLICA IDENTITY. De lo contrario, la tabla no podrá entrar en estado
data syncing.La creación automática no se activa si no se ha creado ninguna tabla de DuckDB en la base de datos actual.
Política de derivación
Cuando una sentencia SQL no cumple las condiciones para ser ejecutada por DuckDB, el parámetro rds_duckdb.enable_fallback determina el comportamiento:
on (predeterminado): La consulta se deriva automáticamente a PostgreSQL. Puede configurar
enable_log_warningpara consultar el motivo de la derivación.off: Se devuelve un error directamente para exponer el problema.
Además, se admite la derivación por retrasos excesivos en la sincronización incremental. El parámetro rds_duckdb.wait_sync_timeout especifica el tiempo máximo que una consulta espera a la posición de sincronización incremental. Unidad: milisegundos:
|
Valor |
Descripción |
|
|
No se comprueba la posición de sincronización y la consulta se ejecuta directamente. |
|
|
El LSN se compara directamente sin esperar. |
|
|
La consulta espera hasta el número especificado de milisegundos. Si la posición de sincronización de DuckDB aún está rezagada respecto a la posición de confirmación de la transacción actual, se activa la derivación o se devuelve un error. |
Escenario de activación: Si las escrituras son frecuentes y el retraso de sincronización es alto, las consultas que requieren una consistencia fuerte podrían derivarse debido a un tiempo de espera agotado.
Ejemplo de registro (enable_log_warning = on):
WARNING: Fallback postgres due to waiting for incremental synchronization timeout
Si enable_fallback = off, se devuelve un error:
ERROR: RDS DuckDB: canceling statement due to waiting for incremental synchronization timeout
Verificar el retraso de sincronización:
-- View synchronization lag
SELECT slot_name, pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS lag_bytes
FROM pg_replication_slots
WHERE slot_name LIKE 'rds_duckdb_slot%';
Umbral de costo de ejecución (plan_cost_threshold)
El parámetro rds_duckdb.plan_cost_threshold garantiza que solo las consultas cuyos costos estimados sean suficientemente altos se reenvíen a DuckDB. Esto evita que las consultas simples se ejecuten más lento debido a la sobrecarga de reenvío e inicio de DuckDB.
|
Parámetro |
Valor predeterminado |
Descripción |
|
|
|
El umbral de costo, en la misma unidad que el |
Escenarios típicos:
Una carga de trabajo en línea contiene tanto sentencias SQL analíticas complejas (JOIN, agregaciones y escaneos grandes) como consultas puntuales simples o escaneos de rango pequeño. Estas últimas se ejecutan más rápido en PostgreSQL porque DuckDB incurre en una sobrecarga fija por el reenvío y la inicialización de la conexión.
Si establece un umbral adecuado, como
1000o10000, las consultas simples de bajo costo continúan ejecutándose en PostgreSQL, y solo las consultas pesadas de alto costo se envían a DuckDB para su aceleración.
Ejemplo de configuración:
-- Set the cost threshold to 5000. Only queries whose PostgreSQL plan costs exceed 5000 are forwarded to DuckDB
SET rds_duckdb.plan_cost_threshold = 5000;
-- Example 1: A simple point query whose PostgreSQL plan cost is low remains in PostgreSQL
/*+ set(rds_duckdb.execution on) */ EXPLAIN SELECT * FROM my_table WHERE id = 1;
-- Example 2: A complex aggregation query whose PostgreSQL plan cost is high is forwarded to DuckDB
/*+ set(rds_duckdb.execution on) */ EXPLAIN SELECT count(*), avg(amount) FROM my_table GROUP BY region;
Este parámetro afecta únicamente a la etapa de evaluación de costos y no influye en el mecanismo de derivación. Incluso si una consulta se reenvía a DuckDB, seguirá derivándose a PostgreSQL si ocurre un error dentro del ejecutor de DuckDB.
Si el umbral es demasiado alto, las consultas de costo medio que podrían acelerarse permanecerán en PostgreSQL. Si el umbral es demasiado bajo, no se podrán filtrar las consultas simples. Ajuste el umbral gradualmente según su carga de trabajo real.
Preguntas frecuentes
¿Por qué una consulta no es ejecutada por DuckDB?
El parámetro rds_duckdb.enable_fallback ayuda a identificar el motivo específico por el cual una consulta no es ejecutada por DuckDB.
Pasos para la solución de problemas:
-- Step 1: Disable fallback to expose the actual cause
SET rds_duckdb.enable_fallback = off;
-- Step 2: Run the target SQL statement
/*+ set(rds_duckdb.execution on) */ EXPLAIN SELECT * FROM my_table;
Posibles errores y sus significados:
|
Error |
Significado |
|
|
La tabla no tiene una tabla orientada a columnas de DuckDB correspondiente o no está en estado de sincronización. |
|
|
Cuando |
|
|
El retraso de sincronización supera |
|
|
Se intentó realizar una operación INSERT, UPDATE o DELETE en una tabla de DuckDB. |
Escenarios comunes de derivación:
|
Escenario |
Descripción |
|
La tabla no es una tabla de DuckDB |
No se llamó a |
|
La tabla no está en estado de sincronización |
La sincronización inicial está incompleta, ocurrió un conflicto DDL o la tabla no tiene clave primaria. |
|
Tiempo de espera de sincronización incremental agotado |
La espera por la sincronización excede el tiempo definido en |
|
Sintaxis no compatible con DuckDB |
DuckDB no admite algunas funciones SQL y la consulta se deriva automáticamente. |
|
La consulta involucra tablas que no son de DuckDB |
Algunas tablas en una consulta JOIN no tienen tablas de DuckDB correspondientes. |
Consulte el estado de sincronización de cada tabla de DuckDB mediante la vista rds_duckdb.duckdb_sync_stat. Se requiere una cuenta con privilegios. Ejemplo:
SELECT sync_table,
sync_status_description,
sync_error_description
FROM rds_duckdb.duckdb_sync_stat;
sync_table | sync_status_description | sync_error_description
-------------------+-------------------------+------------------------------------------
test_schema.test1 | not syncing | no primary key or replica identity index
Si desea consultar datos de tablas que no están sincronizadas incrementalmente, establezca el parámetro rds_duckdb.query_syncing_table = off.