CDC baseado em log: configuração para Postgres, MySQL e mais

Como funciona o CDC baseado em log e o que PostgreSQL, MySQL, SQL Server, Oracle e MongoDB precisam para capturar exclusões e updates.

CDC

Imagine uma sincronização de hora em hora que pede à tabela de pedidos todas as linhas com updated_at maior que o timestamp da execução anterior. Ela então carrega a resposta e reporta sucesso. Mas e um pedido que um cliente apagou hoje à tarde? Ele nunca volta, porque essa linha não existe mais. Por isso, ele continua no warehouse e é contado no dashboard de receita.

O change data capture (CDC) baseado em log, por outro lado, lê o mesmo log que o banco já gera para cada insert, update e delete. A Erathos agora oferece CDC baseado em log para PostgreSQL, MySQL, SQL Server, Oracle e MongoDB. Cada banco tem seu próprio formato de log, que exige configurações diferentes para ser lido. E cada um dos cinco também tem suas próprias formas de falhar quando o log se esgota.

Change Data Capture na Erathos: cada insert, update e delete capturado


Por que sincronizações por cursor perdem dados

Uma sincronização por cursor (a Erathos chama de sincronização parcial) cria um cursor (ou marcador) com o valor mais recente de updated_at que já buscou, e só busca as alterações que acontecem depois desse cursor. Isso a torna barata, mas deixa três coisas fora de alcance:

  • Exclusões: linhas apagadas não voltam em nenhuma consulta e, portanto, nunca serão atualizadas no seu warehouse até alguém recarregar a tabela.
  • Estados intermediários: uma linha atualizada várias vezes entre duas sincronizações volta só uma vez, com a última atualização. Então, se você está montando um histórico de alterações, vai perder os estados intermediários.
  • Timestamps ausentes ou pouco confiáveis: muitas tabelas de produção não têm uma coluna updated_at ou, quando têm, alguns tipos de atualização não a modificam. Se você usa a data de criação como cursor, vai perder todas as atualizações.

Reproduzimos os dois primeiros casos no PostgreSQL com uma tabela de pedidos de três linhas. Depois da última execução, o pedido 1 mudou duas vezes (para paid e depois para shipped) e o pedido 2 foi apagado. A consulta por cursor encontrou uma linha:

SELECT id, status FROM orders WHERE updated_at > :'last_run';

id | status
----+---------
1 | shipped

Repare que o estado paid não aparece mais e que a saída não indica que o pedido 2 foi apagado. Para ver em mais detalhes as diferenças entre cada abordagem, leia este post comparando sincronização por cursor e CDC.

Veja abaixo como essa exclusão apareceria dependendo do tipo de sincronização que você usa:

Why cursor sync misses hard deletes

Como funciona o CDC baseado em log?

O CDC baseado em log lê o log de mudanças que o banco já grava para recuperação após falhas ou para replicação, e repassa ao warehouse todos os inserts, updates e deletes na ordem em que aconteceram. Como ele lê o log de mudanças, e não as tabelas, enxerga exclusões e estados intermediários, e nunca precisa varrer as tabelas de origem atrás de mudanças.

O log de mudanças tem nomes diferentes em cada banco. No PostgreSQL é o write-ahead log (WAL), no MySQL o binary log, no SQL Server o transaction log, no Oracle os redo logs e no MongoDB o oplog. O leitor também guarda até onde chegou, para que cada sincronização comece de onde a anterior terminou.

Estas são as mesmas três mudanças do teste acima, lidas do WAL do PostgreSQL por meio de um slot de replicação lógica:

SELECT data FROM pg_logical_slot_get_changes('demo_slot', NULL, NULL);

BEGIN 755
table public.orders: UPDATE: id[integer]:1 status[text]:'paid' amount[numeric]:100.00 updated_at[timestamp with time zone]:'2026-10-21 17:43:07.121892+00'
COMMIT 755
BEGIN 756
table public.orders: UPDATE: id[integer]:1 status[text]:'shipped' amount[numeric]:100.00 updated_at[timestamp with time zone]:'2026-10-21 17:43:07.12316+00'
COMMIT 756
BEGIN 757
table public.orders: DELETE: id[integer]:2
COMMIT 757

As atualizações chegam na ordem correta, e o mesmo vale para a exclusão. A exclusão traz apenas a chave primária, e é por isso que a configuração do PostgreSQL mais abaixo pede que a linha antiga completa seja registrada no log para a tabela.

A Erathos grava as mudanças extraídas durante uma sincronização em arquivos temporários em um bucket na nuvem (S3, Google Cloud Storage ou Azure Blob Storage) dentro da infraestrutura da Erathos. O warehouse então carrega esses arquivos em lote com COPY ou o seu próprio comando de carga, e a Erathos apaga os arquivos temporários ao terminar. O banco de produção fica livre do trabalho pesado. Isso faz sentido, porque um banco transacional é feito para muitas gravações pequenas, e um warehouse para leituras grandes (OLTP vs OLAP).

Este é o fluxo completo, do log do banco até o warehouse:

How log-based CDC reaches the warehouse

PostgreSQL: lendo o WAL

Todas as mudanças na tabela são gravadas no WAL (write-ahead log) do PostgreSQL antes de os próprios arquivos da tabela serem modificados. Se o wal_level estiver definido como logical, o WAL contém informação suficiente para recriar as mudanças linha a linha (esse processo se chama logical decoding). Um slot de replicação acompanha a posição do leitor no WAL.

O conector de PostgreSQL da Erathos exige:

  1. Definir wal_level como logical. O valor padrão é replica.
  2. Permitir pelo menos 10 slots de replicação e 10 WAL senders. O valor padrão dos dois é 10, então muitos servidores já atendem a esse requisito. Veja a documentação do PostgreSQL.
  3. Definir max_slot_wal_keep_size com um valor maior que 0. O valor padrão de -1 permite que o slot retenha WAL sem limite (e pode encher o disco se o leitor parar). Veja a documentação do PostgreSQL.
  4. REPLICA IDENTITY FULL em cada tabela que você quer capturar. Por padrão, o PostgreSQL registra apenas os valores antigos da chave primária em updates e deletes.
  5. Um usuário no PostgreSQL com o privilégio REPLICATION.

Estes comandos cobrem os cinco itens. O limite de WAL vale com um reload, e as outras três configurações do servidor exigem reinício:

ALTER SYSTEM SET wal_level = 'logical';
ALTER SYSTEM SET max_replication_slots = 10;
ALTER SYSTEM SET max_wal_senders = 10;
ALTER SYSTEM SET max_slot_wal_keep_size = '5GB';

-- after restarting PostgreSQL:
ALTER TABLE orders REPLICA IDENTITY FULL;
ALTER ROLE some_user WITH REPLICATION;

Depois do reinício, esta verificação mostra os novos valores. O PostgreSQL informa o limite de WAL em megabytes, então 5120 corresponde aos 5 GB definidos acima:

SELECT name, setting FROM pg_settings
WHERE name IN ('wal_level', 'max_replication_slots', 'max_wal_senders', 'max_slot_wal_keep_size')
ORDER BY name;

name | setting
------------------------+---------
max_replication_slots | 10
max_slot_wal_keep_size | 5120
max_wal_senders | 10
wal_level | logical

Com REPLICA IDENTITY FULL ativado, apagar o pedido 3 na tabela de teste gerou uma mudança com a linha antiga inteira, e não apenas o id:

table public.orders: DELETE: id[integer]:3 status[text]:'pending' amount[numeric]:300.00 updated_at[timestamp with time zone]:'2026-10-21 17:43:07.104481+00'

O lado negativo é que mais WAL é gerado. O nível de replicação logical aumenta a quantidade de WAL gravada, principalmente se muitas tabelas estiverem configuradas com REPLICA IDENTITY FULL e tiverem muitos UPDATEs e DELETEs.

MySQL: lendo o binlog

O conector de MySQL da Erathos captura as mudanças de dados em bancos MySQL usando o binary log (binlog), o log usado na replicação do MySQL. Esse log precisa estar configurado no formato ROW com imagens de linha FULL, para que updates e deletes tragam todas as colunas no evento.

A partir do MySQL 8.0, o binary log vem ativado por padrão e as duas configurações já vêm com os valores de que o CDC precisa. Um servidor MySQL com as configurações intocadas retornou isto:

SHOW VARIABLES WHERE Variable_name IN (
'log_bin',
'binlog_format',
'binlog_row_image',
'binlog_expire_logs_seconds',
'binlog_row_value_options'
);

+----------------------------+---------+
| Variable_name | Value |
+----------------------------+---------+
| binlog_expire_logs_seconds | 2592000 |
| binlog_format | ROW |
| binlog_row_image | FULL |
| binlog_row_value_options | |
| log_bin | ON |
+----------------------------+---------+

Um servidor configurado com a imagem de linha minimal vai incluir apenas a chave primária na imagem anterior (e, portanto, nenhum outro valor da linha apagada). Essas configurações exigem alterar o my.cnf e reiniciar o MySQL.

O usuário de CDC precisa destas permissões. As três primeiras são usadas apenas durante o snapshot:

GRANT SELECT, RELOAD, SHOW DATABASES, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'your_existing_user';

Veja neste post como ativar o binlog e carregar o primeiro conjunto de dados no BigQuery: MySQL CDC to BigQuery

SQL Server: tabelas de mudança do CDC

O SQL Server tem um recurso de CDC próprio. Quando ativado, um job de captura lê o transaction log e grava as mudanças em tabelas de mudança no schema cdc, que a Erathos pode então ler. Por isso, o conector de SQL Server da Erathos tem quatro requisitos:

  1. O CDC precisa estar ativado no banco com sys.sp_cdc_enable_db, que deve ser chamado por um usuário com login sysadmin ou por um membro do papel db_owner no Azure SQL Database. Se você está no AWS RDS, a chamada equivalente é msdb.dbo.rds_cdc_enable_db.
  2. O CDC precisa estar ativado nas tabelas com sys.sp_cdc_enable_table. Isso significa que não é possível capturar mudanças em tabelas sem o CDC ativado.
  3. O SQL Server Agent precisa estar ativo, porque é ele que roda os jobs de captura e de limpeza. No Azure SQL Database não existe Agent, e as mudanças são capturadas por um agendador interno.
  4. É preciso criar um usuário com acesso de leitura às tabelas capturadas e ao schema cdc.

Esta é a configuração para um banco e uma tabela:

SELECT name, is_cdc_enabled FROM sys.databases WHERE name = 'shop';

USE shop;
EXEC sys.sp_cdc_enable_db;

EXEC sys.sp_cdc_enable_table
@source_schema = N'dbo',
@source_name = N'orders',
@role_name = NULL,
@supports_net_changes = 0;

GRANT SELECT ON SCHEMA::dbo TO erathos;
GRANT SELECT ON SCHEMA::cdc TO erathos;

O CDC tem dois padrões importantes:

E o CDC tem até algum efeito sobre o próprio transaction log. Mesmo no modo de recuperação simples, o SQL Server não consegue truncar o transaction log até que o job de captura tenha lido as mudanças. Isso significa que, se o job do Agent parar, o transaction log vai continuar crescendo.

Oracle: redo logs e archive logs pelo LogMiner

Os online redo logs são os arquivos que o Oracle usa para guardar as mudanças feitas no banco, e são gravados de forma circular. Quando o banco está em modo ARCHIVELOG, cada arquivo de redo log cheio é copiado para um archive log antes de ser sobrescrito. A Erathos usa o LogMiner, uma ferramenta do Oracle para ler redo logs com SQL, para ler tanto os online redo logs quanto os arquivados.

Estes são os pré-requisitos do conector de Oracle da Erathos:

  1. Modo ARCHIVELOG: vem desativado por padrão em bancos recém-criados, e ativá-lo exige reiniciar o banco.
  2. Supplemental logging ativado: o supplemental logging precisa estar ativado no nível do banco, e o supplemental logging de todas as colunas precisa estar ativado em cada tabela capturada. O Oracle não ativa o supplemental logging por padrão, e o LogMiner não pode ser usado sem ele. Ativar o supplemental logging no nível do banco com o logging mínimo não adiciona uma sobrecarga significativa.
  3. Usuário de CDC: é preciso um usuário do banco com os privilégios necessários para usar o LogMiner (por exemplo, LOGMINING e EXECUTE_CATALOG_ROLE) para usar o conector de CDC do Oracle. Ele precisa ser um usuário comum (por exemplo, c##erathos_cdc) se você usa um banco multitenant. Consulte a documentação da Erathos para a lista de privilégios necessários.

Estes comandos verificam e ativam os dois primeiros itens, rodando como SYSDBA:

SELECT LOG_MODE FROM V$DATABASE;
ALTER DATABASE ARCHIVELOG;

ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;
ALTER TABLE shop.orders ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;
SELECT SUPPLEMENTAL_LOG_DATA_MIN FROM V$DATABASE;

Se archive logs necessários para o CDC forem apagados, a conexão precisa de um novo snapshot inicial. Isso pode acontecer, por exemplo, se houver uma política de backup configurada no RMAN, a ferramenta de backup do Oracle, ou se a área de recuperação encher. Também é possível que um provedor de serviço gerenciado imponha restrições ao archive logging, ao supplemental logging ou ao LogMiner (como no Autonomous Database da Oracle) e, nesse caso, o CDC pode não funcionar.

MongoDB: o oplog e os change streams

O oplog do MongoDB é uma coleção de tamanho fixo que guarda todas as operações de escrita. A Erathos usa change streams para acompanhar as mudanças no oplog. Change streams só podem ser usados em replica sets e clusters sharded, e o conector de MongoDB da Erathos exige:

  • Um replica set ou cluster sharded. O db.hello().setName retorna o nome do replica set, ou nada se a instância do MongoDB for um servidor standalone. Os clusters do MongoDB Atlas são sempre replica sets.
  • Um usuário com o papel nativo readAnyDatabase. Ele permite ao usuário ler bancos, listá-los e abrir change streams.
  • Uma janela de oplog maior que o intervalo entre sincronizações. O oplog descarta as entradas mais antigas quando atinge o limite de tamanho. O tamanho padrão do oplog é 5% do espaço em disco disponível (de 990 MB a 50 GB).

As exclusões pedem mais uma decisão. Por padrão, um evento de exclusão traz apenas o _id do documento. A partir do MongoDB 6.0, ativar pre-images em uma coleção adiciona o documento completo como ele era antes da exclusão:

db.runCommand({ collMod: "orders", changeStreamPreAndPostImages: { enabled: true } })

Em um replica set de teste, apagar um documento antes desse comando e outro depois gerou estes dois eventos (resumidos):

{
operationType: 'delete',
ns: { db: 'shop', coll: 'orders' },
documentKey: { _id: 1 }
}
{
operationType: 'delete',
ns: { db: 'shop', coll: 'orders' },
documentKey: { _id: 2 },
fullDocumentBeforeChange: { _id: 2, status: 'pending', amount: 200 }
}

Lembre que as pre-images ocupam espaço de armazenamento e tempo de processamento e fazem mais sentido nas coleções em que você precisa dos campos do documento apagado no warehouse.

Os cinco bancos lado a lado

Banco

Log de mudanças lido

Principal passo de configuração

Risco de retenção

Como chegam as exclusões

PostgreSQL

WAL, por meio de um slot de replicação lógica

wal_level = logical, mais REPLICA IDENTITY FULL por tabela

Por padrão, um slot retém WAL sem limite e pode encher o disco

Só a chave primária; linha antiga completa com REPLICA IDENTITY FULL

MySQL

Binary log

Formato ROW com imagens de linha FULL

Os arquivos de binlog expiram depois de 30 dias por padrão

Linha antiga completa com imagens de linha FULL

SQL Server

Tabelas de mudança do CDC, preenchidas a partir do transaction log

sp_cdc_enable_db e sp_cdc_enable_table, com o Agent rodando

O job de limpeza apaga as mudanças depois de 3 dias por padrão

Uma linha de mudança com os valores apagados

Oracle

Redo logs e archive logs, pelo LogMiner

Modo ARCHIVELOG mais supplemental logging

Archive logs apagados por uma política de backup ou por uma área de recuperação cheia

Linha antiga completa com supplemental logging de todas as colunas

MongoDB

Oplog, por meio de change streams

Replica set ou cluster sharded

As entradas mais antigas do oplog são descartadas quando ele atinge o limite de tamanho

Só o _id; documento completo com pre-images (6.0+)

Os mesmos cinco, de relance:

What CDC reads on each database

Modos de snapshot: initial ou no_data

Ao configurar uma nova conexão de CDC, é preciso decidir se as linhas que já existem nas tabelas capturadas entram ou não. Na Erathos, essa configuração se chama cdc_snapshot_mode e funciona do mesmo jeito para os cinco bancos.

  • initial: copia cada tabela capturada uma vez e, depois disso, transmite as mudanças a partir do log.
  • no_data: não copia nenhum dado. Só transmite as mudanças restantes no log. Linhas que não mudarem não vão aparecer.

O initial é uma boa escolha para quase todas as conexões novas, já que o warehouse começa vazio e queremos capturar o conjunto atual de linhas. Como o snapshot inicial é o único momento em que o CDC lê tabelas inteiras, é a operação mais importante para agendar em horários de pouco movimento.

O no_data é útil quando queremos apenas as mudanças a partir de certo ponto, como uma trilha de auditoria de edições. Também pode ser usado se as linhas existentes já estão no warehouse e copiá-las de novo seria redundante. Em todo caso, não vamos receber nenhuma mudança anterior à entrada mais antiga disponível no log, então o warehouse precisa ter uma cópia atualizada.

Outro uso de um novo snapshot initial é recuperar entradas do log que se perderam antes de o CDC processá-las.

Retenção do log: o risco que os cinco compartilham

Cada um dos 5 logs tem um fim. Quando as mudanças mais antigas são removidas do log antes de o CDC conseguir lê-las, não há nada que consiga reproduzi-las. A conexão precisa fazer um novo snapshot inicial para se recuperar.

When the log runs out before CDC reads it

O PostgreSQL falha pelo caminho contrário. Um slot de replicação retém o WAL até o leitor confirmar a leitura, então um slot parado cresce até o disco encher. Com um limite de max_slot_wal_keep_size, o servidor remove o WAL acima desse tamanho mesmo que o slot ainda precise dele, e você volta a precisar de um snapshot, mas com o disco intacto. No banco de teste, um slot sem leitura reteve 63 MB de WAL depois de um único insert de 200.000 linhas pequenas:

SELECT slot_name, active,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal
FROM pg_replication_slots
ORDER BY slot_name;

slot_name | active | retained_wal
-----------+--------+--------------
demo_slot | f | 63 MB
idle_slot | f | 63 MB

A retenção precisa ser maior que o maior intervalo imaginável entre duas leituras de CDC da Erathos. Pense, por exemplo, em uma replicação pausada, uma falha na sexta à noite ou uma indisponibilidade do warehouse.

  • PostgreSQL. O max_slot_wal_keep_size pode ser definido de acordo com o espaço livre em disco (a Erathos usa 5 GB como exemplo). A consulta acima mostra o WAL retido. Os slots de replicação da Erathos têm o prefixo erathos-cdc.
  • MySQL. O binlog_expire_logs_seconds tem padrão de 30 dias e deve funcionar para a maioria dos agendamentos. Para servidores com um valor menor, a Erathos recomenda alterar o valor no my.cnf para 864000 (10 dias).
  • SQL Server. Aumente a retenção do job de limpeza (padrão de 3 dias) para cobrir feriados prolongados. Qualquer mudança no job só tem efeito depois de parar e reiniciar o job.
  • Oracle. Garanta que os archive logs fiquem guardados por mais tempo que o maior intervalo possível entre duas sincronizações. Confira a política de exclusão do RMAN. Garanta que a área de recuperação tenha espaço suficiente para não encher.
  • MongoDB. Defina um período mínimo de retenção para o oplog (a Erathos usa 24 horas como exemplo). O oplog pode ser redimensionado sem reiniciar o MongoDB. Coleções movimentadas enchem o oplog mais rápido. Confira a janela do oplog com rs.printReplicationInfo().

No SQL Server, este comando define a retenção em 7 dias (10080 minutos):

EXEC sys.sp_cdc_change_job @job_type = N'cleanup', @retention = 10080;

Quando o CDC vale a pena?

O CDC exige mais do banco de origem do que uma sincronização por cursor: mudanças de configuração, às vezes um reinício e um log para acompanhar. Esse custo compensa em algumas tabelas e não em outras. Na Erathos, o tipo de atualização é escolhido por tabela, então uma mesma conexão pode misturar os tipos:

A tabela

Melhor opção

Tem linhas apagadas fisicamente (hard delete)

CDC

Não tem uma coluna updated_at confiável

CDC, ou full refresh se for pequena

Você precisa guardar cada mudança como histórico

CDC

É grande, com uma pequena parte das linhas mudando

CDC

É uma tabela pequena de referência ou de configurações

Full refresh

Tem updated_at confiável, nunca apaga linhas e só o estado mais recente importa

Sincronização por cursor (parcial)

Para tabelas muito pequenas, o Full Refresh (recarregar a tabela inteira) captura as exclusões. Se houver uma chave primária e uma coluna de cursor, a sincronização por cursor é a melhor opção para tabelas que só recebem inserções ou que têm as atualizações registradas em data.

O CDC está disponível no plano Pro da Erathos (atualização a cada 5 minutos).

Perguntas frequentes

O CDC sobrecarrega o meu banco de produção?

Depois do primeiro snapshot, o CDC só olha o log, e não as tabelas. O primeiro snapshot, porém, lê todas as tabelas capturadas uma vez. O custo contínuo fica no logging do servidor do banco: o PostgreSQL grava mais WAL, um job de captura do SQL Server roda no servidor, as pre-images do MongoDB ocupam espaço de armazenamento e cada engine retém mais log.

Preciso de CDC para ter dados em tempo real?

Não. O CDC (Change Data Capture) também captura exclusões e estados intermediários, que as sincronizações por cursor perdem. A Erathos roda o CDC de forma agendada (atualização a cada 5 minutos no plano Pro), e a sincronização por cursor pode rodar com a mesma frequência.

O que acontece com as exclusões no warehouse?

Com CDC, cada evento de exclusão chega ao pipeline como uma mudança própria, com a chave e os dados completos da linha antiga (com as configurações citadas acima: REPLICA IDENTITY FULL, imagens de linha FULL, supplemental logging de todas as colunas, pre-images do MongoDB). Com a sincronização por cursor, as linhas apagadas só saem do warehouse quando é feita uma recarga completa.

Preciso reiniciar o meu banco?

Depende do banco e da configuração anterior:

  • PostgreSQL: sim, para ativar o wal_level, os slots de replicação e os WAL senders. O limite de WAL exige apenas um reload.
  • MySQL: sim, mas só se você alterar as configurações do binlog; o MySQL 8.0 já vem com ROW e FULL por padrão.
  • SQL Server: o CDC é ativado com stored procedures; a documentação da Erathos não indica reinício.
  • Oracle: sim, para ativar o modo ARCHIVELOG.
  • MongoDB: não, para redimensionar o oplog. Se você estiver convertendo um servidor standalone em replica set, isso exige reinício.

Toda conta nova criada na Erathos começa no plano Pro, que inclui CDC, grátis por 14 dias e sem cartão de crédito. Comece seu teste grátis e conecte o banco que você configurou acima.