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.

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.
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:
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:
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:
- Definir
wal_levelcomological. O valor padrão éreplica. - 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.
- Definir
max_slot_wal_keep_sizecom 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. REPLICA IDENTITY FULLem cada tabela que você quer capturar. Por padrão, o PostgreSQL registra apenas os valores antigos da chave primária em updates e deletes.- 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_settingsWHERE 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:
- O CDC precisa estar ativado no banco com
sys.sp_cdc_enable_db, que deve ser chamado por um usuário com loginsysadminou por um membro do papeldb_ownerno Azure SQL Database. Se você está no AWS RDS, a chamada equivalente émsdb.dbo.rds_cdc_enable_db. - 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. - 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.
- É 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:
- As mudanças expiram depois de 3 dias: o job de limpeza guarda as linhas de mudança por 4320 minutos antes de apagá-las. Se essas linhas não tiverem sido lidas pela Erathos, é preciso fazer um novo snapshot da conexão.
- Colunas novas são ignoradas: a instância de captura só usa as colunas com que foi configurada quando foi criada. Para adicionar colunas a uma tabela com CDC ativado, você precisa criar uma segunda instância de captura com as novas colunas e depois remover a antiga (o SQL Server só permite até duas instâncias de captura por tabela).
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:
- Modo ARCHIVELOG: vem desativado por padrão em bancos recém-criados, e ativá-lo exige reiniciar o banco.
- 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.
- Usuário de CDC: é preciso um usuário do banco com os privilégios necessários para usar o LogMiner (por exemplo,
LOGMININGeEXECUTE_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().setNameretorna 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 |
| Por padrão, um slot retém WAL sem limite e pode encher o disco | Só a chave primária; linha antiga completa com |
MySQL | Binary log | Formato | Os arquivos de binlog expiram depois de 30 dias por padrão | Linha antiga completa com imagens de linha |
SQL Server | Tabelas de mudança do CDC, preenchidas a partir do transaction log |
| 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 |
Os mesmos cinco, de relance:
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.
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_walFROM pg_replication_slotsORDER 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_sizepode 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 prefixoerathos-cdc. - MySQL. O
binlog_expire_logs_secondstem 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 nomy.cnfpara 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 | 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 | 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.