Todos os produtos
Search
Central de documentação

ApsaraDB for MongoDB:Capture alterações de dados em tempo real com change streams do MongoDB

Última atualização: Jun 26, 2026

Para responder a alterações no banco de dados em tempo real, assine os change streams do MongoDB. Este tópico explica o que são change streams, como utilizá-los e apresenta as melhores práticas.

O que é um change stream?

Um change stream converte eventos de alteração do banco de dados em um fluxo em tempo real. Os clientes podem assinar esse fluxo e receber notificações imediatas sempre que dados forem inseridos, atualizados ou excluídos. Os cenários típicos incluem:

  • Sincronização de dados entre clusters: replique dados incrementalmente entre clusters MongoDB.

  • Auditoria de operações: rastreie operações de alto risco, como a exclusão de um banco de dados ou coleção.

  • Arquitetura orientada a eventos: envie alterações para sistemas downstream para análises em tempo real, atualizações de cache ou notificações.

Histórico de versões

Versão

Descrição da atualização

MongoDB 3.6

  • Lançamento inicial.

  • Suporta apenas assinaturas no nível da coleção.

  • Tipos de events limitados.

  • Suporta recuperação de falhas.

  • Permite visualizar a post-image (estado do documento após a alteração).

MongoDB 4.0

  • Suporta assinaturas nos níveis de banco de dados e cluster.

  • Suporta eventos drop, dropDatabase e rename.

  • O formato do resumeToken mudou de BinData para Hex.

MongoDB 4.2

  • Suporta mais operadores de pipeline, como $set e $unset.

  • Adiciona a opção startAfter para iniciar a escuta a partir de um ponto específico no tempo.

  • Modifique o campo _id em um evento faz o change stream lançar uma exceção.

  • Remove a dependência de {readConcern: majority}.

MongoDB 5.1

  • Melhora a eficiência de execução de certos estágios do pipeline de agregação.

  • Aumenta a eficiência na utilização de recursos.

MongoDB 5.3

  • Filtra atualizações em documentos órfãos durante a migração de chunks.

MongoDB 6.0

  • Permite visualizar a pre-image (estado do documento antes da alteração).

  • É possível usar instruções DDL como create, createIndexes, modify e shardCollection ao especificar showExpandedEvents:true. Para mais informações, consulte Change Events.

  • Os eventos de alteração agora incluem um campo wallTime. Os timestamps suportam vários operadores de transformação e exibição (incluindo $toDate, $tsSeconds e $tsIncrement) para simplificar o consumo pelas aplicações.

MongoDB 7.0

  • Suporta eventos de alteração muito grandes (>16 MB) com o novo operador $changeStreamSplitLargeEvent para dividir eventos excessivamente grandes.

  • Os eventos de alteração passam a suportar eventos refineCollectionShardKey e reshardCollection.

MongoDB 8.0

  • O comando $queryStats aprimora as métricas relacionadas aos change streams.

  • O comando movePrimary não gera mais eventos inválidos para tabelas com change streams ativos. Agora, os change streams processam continuamente migrações de dados causadas pelo comando movePrimary.

Limites

Tipos de instância suportados: instância de replica set ou instância de sharded cluster.

Configurar change stream

Escutar eventos DDL adicionais

Pré-requisitos

MongoDB 6.0 ou posterior (Guia de upgrade).

Procedimento

  1. Use o mongosh para conectar-se ao banco de dados.

  2. Execute o comando watch com showExpandedEvents: true:

    // mongo shell or mongosh v1.x
    cursor = db.getSiblingDB("test").watch([],
      {
        showExpandedEvents: true       // Enable listening for more DDL events
      }
    );
    cursor.next();

Verificar resultados

  1. Em uma nova janela SQL, execute uma instrução de alteração, como db.createCollection("myCollection1").

  2. Verifique na janela SQL original a saída relacionada à instrução executada.

    mg xxx test> cursor = db.getSiblingDB("test").watch([],
    ... {
    ...     showExpandedEvents: true     // Enable more DDL event listening
    ... }
    ... );
    ... cursor.next();
    Warning: If there are no documents in the batch, next will block. Use tryNext if you want to check if there are any documents without waiting.
    {
      _id: {
        _data: '8268777614000000012B042C0100296E5A100434919C64814940F3A61F9D97A3F60D5C463C6Fxxx03C63726561746500466F70657274696F6E4465737372697074696F6E0046466964496E6465787800461E76002B04466B657990046E5F6964002B02003C6E616D65003C5F69645F000000004'
      },
      operationType: 'create',
      clusterTime: Timestamp({ t: 1752659476, i: 1 }),
      collectionUUID: UUID('34919c64-8149-40f3-a61f-9xxx'),
      wallTime: ISODate('2025-07-16T09:51:16.043Z'),
      ns: { db: 'test', coll: 'myCollection1' },
      operationDescription: { idIndex: { v: 2, key: { _id: 1 }, name: '_id_' } }
    }

Ativar pre-image

No MongoDB, uma pre-image é um snapshot completo de um documento antes da modificação ou exclusão. Ela registra os valores originais antes que a alteração ocorra.

Pré-requisitos

MongoDB 6.0 ou posterior (Guia de upgrade).

Procedimento

  1. Ative a pre-image no nível do banco de dados:

    db.adminCommand({
      setClusterParameter: {
        changeStreamOptions: {
          preAndPostImages: { expireAfterSeconds: "off" } // "off" uses oplog retention period
        }
      }
    })
  2. Ative a pre-image no nível da coleção:

    Nota

    Para ativar a pre-image para todas as coleções de um banco de dados, primeiro ative-a no nível do banco de dados. Em seguida, ative-a separadamente para cada coleção desse banco.

    Modifique uma coleção existente

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

    Especifique ao criar uma nova coleção

    db.createCollection("myCollection", { changeStreamPreAndPostImages: { enabled: true }})
  3. Crie um listener de change stream (especifique as opções de pre-image):

    // Create a listener on the target collection
    cursor = db.getCollection("myCollection").watch([],
      {
        fullDocument: 'required', // or 'whenAvailable'
        fullDocumentBeforeChange: 'required' // or 'whenAvailable'
      }
    )
    cursor.next();
    • required: o servidor deve retornar a pre/post-image. Caso contrário, retorna um erro.

    • whenAvailable: o servidor tenta retornar a imagem, mas não garante seu retorno.

Verificar resultados

  1. Verifique se a configuração no nível do banco de dados está ativada:

    db.adminCommand( { getClusterParameter: "changeStreamOptions" } )

    Se bem-sucedido, o comando retorna uma saída semelhante à seguinte:

    test> db.adminCommand( { getClusterParameter: "changeStreamOptions" } )
    {
      clusterParameters: [
        {
          _id: 'changeStreamOptions',
          clusterParameterTime: Timestamp({ t: 1752655937, i: 1 }),
          preAndPostImages: { expireAfterSeconds: Long('100') }
        }
      ],
      ok: 1,
      '$clusterTime': {
        clusterTime: Timestamp({ t: 1752656717, i: 1 }),
        signature: {
          hash: Binary.createFromBase64('xxx=', 0),
          keyId: Long('xxx')
        }
      },
      operationTime: Timestamp({ t: 1752656717, i: 1 })
    }
  2. Verifique a configuração da coleção:

    db.getCollectionInfos({name: "myCollection"}) // or db.runCommand({listCollections: 1})

    Saída esperada: encontre um campo semelhante a "options" : { "changeStreamPreAndPostImages" : { "enabled" : true } } no documento retornado.

    test> db.getCollectionInfos({name: "myCollection"})
    [
      {
        name: 'myCollection',
        type: 'collection',
        options: { changeStreamPreAndPostImages: { enabled: true } },
        info: {
          readOnly: false,
          uuid: UUID('55ed1b7c-7575-4ba3-8afa-xxx')
        },
        idIndex: { v: 2, key: { _id: 1 }, name: '_id_' }
      }
    ]
  3. Em outra janela do mongosh, atualize um documento em myCollection.

  4. Observe o evento retornado pelo cursor. Ele deve incluir o campo fullDocumentBeforeChange (o documento antes da alteração).

    ... cursor.next();
    {
      _id: {
        _data: '8268786B15000000012B042C0100296E5A100455ED1B7C75754BA38AFA0C5B56A360BC463C6F7065726174696F6E54797065003C7570646174650046646F63756D656E744B65790046xxx697CDDF6EE279xxx'
      },
      operationType: 'update',
      clusterTime: Timestamp({ t: 1752722197, i: 1 }),
      wallTime: ISODate('2025-07-17T03:16:37.626Z'),
      fullDocument: {
        _id: ObjectId('6878697cddfxxx'),
        name: 'test',
        count: 111
      },
      ns: { db: 'test', coll: 'myCollection' },
      documentKey: { _id: ObjectId('6878697cddfxxx') },
      updateDescription: {
        updatedFields: { count: 111 },
        removedFields: [],
        truncatedArrays: []
      },
      fullDocumentBeforeChange: { _id: ObjectId('6878697cddf6xxx'), name: 'test' }
    }

Para mais informações, consulte Change Streams with Document Pre- and Post-Images.

Ativar post-image

No MongoDB, uma post-image é um snapshot completo de um documento após a ocorrência de uma alteração. Ela registra o conteúdo integral do documento depois da mudança.

Pré-requisitos

MongoDB 3.6 ou posterior (Guia de upgrade).

Procedimento

Ao executar o comando watch, defina fullDocument: 'updateLookup'.

cursor = db.getSiblingDB("test").myCollection.watch([], 
  {
    fullDocument: 'updateLookup'
  }
);
cursor.next();
              

Verificar resultados

  1. Em outra janela do mongosh, insira ou atualize um documento em myCollection.

  2. Observe o evento retornado pelo cursor. Ele deve incluir o campo fullDocument (o documento após a alteração).

    xxx [primary] test> cursor = db.getSiblingDB("test").myCollection.watch([],
    ... {
    ...     fullDocument: 'updateLookup'
    ... }
    ... );
    ... cursor.next();
    [...
    {
      _id: {
        _data: 'xxx100296E5A10040D6C3FBC28484F08240555576066945463C6F7065726174696F6E54797065003C757064617465500046646F63756D656E744B6579005F6964004B657390046645F696400646881ADE741E7D4B638ED7CA1000004'
      },
      operationType: 'update',
      clusterTime: Timestamp({ t: 1753329166, i: 1 }),
      wallTime: ISODate('2025-07-24T03:52:46.966Z'),
      fullDocument: {
        _id: ObjectId('xxx8ed7ca1'),
        name: 'test1',
        age: 12,
        count: 2222
      },
      ns: { db: 'test', coll: 'myCollection' },
      documentKey: { _id: ObjectId('xxx7ca1') },
      updateDescription: {
        updatedFields: { count: 2222 },
        removedFields: [],
        truncatedArrays: []
      }
    }]
Nota

O documento completo retornado pode estar vazio ou não refletir um estado preciso em um ponto específico no tempo. Por exemplo:

  • Se o mesmo documento for atualizado várias vezes em rápida sucessão, o evento de alteração da primeira atualização pode retornar o estado do documento após a conclusão da atualização mais recente.

  • Se um documento for atualizado e imediatamente excluído, seu evento de alteração mostrará um campo fullDocument vazio, pois o documento pós-alteração não existe mais.

Para mais informações, consulte Lookup Full Document for Update Operations.

Lidar com eventos de alteração muito grandes (>16 MB)

Pré-requisitos

MongoDB 7.0 ou posterior (Guia de upgrade).

Procedimento

Inclua o estágio $changeStreamSplitLargeEvent no pipeline do comando watch():

myChangeStreamCursor = db.myCollection.watch(
  [ { $changeStreamSplitLargeEvent: {} } ], // Add split stage
  {
    fullDocument: "required",
    fullDocumentBeforeChange: "required"
  }
)

Verificar resultados

  • Realize uma operação que gere um evento de alteração maior que 16 MB (por exemplo, atualizar um documento contendo um array muito grande).

  • Observe que o fluxo de eventos retornado é dividido em vários eventos fragment consecutivos, terminando com um evento fragment final.

Reduzir a sobrecarga de armazenamento de pre-images

Por padrão, as pre-images expiram junto com o oplog. Defina um tempo de expiração menor para economizar espaço:

Aviso

Se você definir expireAfterSeconds com um valor muito curto e seu consumidor downstream não conseguir acompanhar o processamento, poderá ocorrer um erro ChangeStreamHistoryLost (porque a pre-image expira cedo demais). Para detalhes, consulte Change Streams with Document Pre- and Post-Images.

db.adminCommand({
  setClusterParameter: {
    changeStreamOptions: {
      preAndPostImages: { expireAfterSeconds: 100 } // Unit: seconds
    }
  }
})

Melhores práticas para change streams

  1. Use pre-images e post-images com cautela:

    • Ativar fullDocumentBeforeChange (pre-image) e fullDocument (post-image) aumenta a sobrecarga de armazenamento (na coleção config.system.preimages) e a latência das requisições.

    • Ative esses recursos apenas quando sua aplicação realmente precisar do conteúdo completo do documento antes e depois de uma alteração.

  2. Considerações importantes para implantações de sharded cluster:

    • Sempre crie listeners de change stream no mongos para garantir a ordenação global dos eventos.

    • Sob altas cargas de escrita, os change streams podem se tornar um gargalo, pois o mongos precisa ordenar e mesclar eventos provenientes dos shards.

    • Uma distribuição desigual de escritas entre os shards (por exemplo, devido a uma chave de shard mal projetada) aumenta significativamente a latência do change stream.

  3. Evite updateLookup:

    • O updateLookup executa uma consulta findOne separada para cada evento de atualização, o que é ineficiente.

    • Em sharded clusters, as operações de moveChunk agravam ainda mais a latência do updateLookup.

  4. Previna interrupções no change stream:

    • ⚠️ Os cenários a seguir tornam um cursor de change stream inválido (operationType: "invalidate") ou geram erros.

      • Atraso no consumidor downstream: o consumidor processa eventos mais lentamente do que são gerados, fazendo com que o resumeToken fique fora da janela do oplog.

      • **resumeToken inválido: uso de um resumeToken desatualizado cujo timestamp não está mais presente no oplog.

      • Impacto de failover: após um failover, o oplog do novo nó primário pode não conter o resumeToken original.

      • Alterações de metadados: operações como drop, rename e dropDatabase podem disparar um evento de invalidate.

      • Expiração da pre-image: definir expireAfterSeconds com um valor muito curto enquanto o consumo é lento causa perda de pre-images.

    • Estratégias de mitigação:

      • Monitore a latência do change stream.

      • Garanta que a janela do oplog seja suficientemente grande.

      • Implemente lógica robusta de tratamento de erros e recuperação: capture eventos de invalidate, registre o último resumeToken válido e recrie o listener.

      • Defina um valor razoável para expireAfterSeconds.

  5. Estratégia de seleção de escopo:

    • Change stream único vs. múltiplos change streams no nível da coleção:

      • Stream único (nível de banco de dados/instância): menor sobrecarga de recursos (busca de oplog single-threaded), mas exige filtragem e despacho no downstream. Sob alto volume de eventos, o mongos pode se tornar um gargalo.

      • Múltiplos streams no nível da coleção: podem aproveitar a filtragem no lado do servidor para reduzir o tráfego de rede e oferecer melhor concorrência. No entanto, muitos streams aumentam a contenção por leituras do oplog e o consumo de recursos.

    • Recomendação: teste com base na sua carga de trabalho (volume de eventos, número de coleções) e escolha a abordagem ideal. Geralmente, use streams dedicados para um pequeno número de coleções altamente ativas e utilize streams no nível de banco de dados/instância com filtragem downstream para muitas coleções com baixa atividade.

    • FAQ

      **1. Por que os slow logs de change stream sempre mostram COLLSCAN?**

      Esse comportamento é normal e esperado. Nenhuma otimização é necessária. Um cursor de change stream lê, em última instância, de local.oplog.rs (o único local que registra todas as modificações da instância). Essa coleção não possui índices, portanto, sempre realiza um COLLSCAN. Isso não pode ser evitado e não há margem para otimização.

      Se você usar COLLSCAN como palavra-chave para filtrar slow logs em busca de consultas a otimizar, filtre também a palavra-chave $changeStream.

      Investigue slow logs relacionados a change streams apenas se encontrar problemas de desempenho (por exemplo, aumento de latência no consumo downstream).

      2. Por que, em uma instância com shard, vejo cursores de change stream em shards diferentes do shard primário ao escutar uma coleção sem shard?

      Isso prepara o sistema para possíveis operações de shardCollection. Você pode converter uma coleção sem shard com um listener ativo em uma coleção com shard a qualquer momento, distribuindo seus dados por todos os shards (semelhante ao movePrimary). Criar cursores de change stream em outros shards antecipadamente lida com esse cenário. Normalmente, esses cursores em outros shards não retornam eventos de alteração e incorrem em sobrecarga mínima de desempenho.

      Da mesma forma, para lidar com potenciais operações de addShard/removeShard, quando você configura um listener em uma instância com shard, o mongos também cria os cursores correspondentes no Config Server.

      3. Por que o slow log de um cursor de change stream aparece no nó primário mesmo eu tendo especificado readPreference:secondary ao criar o cursor?

      Um cursor fica "fixado" em um nó específico após a criação e não migra automaticamente quando as funções dos nós mudam. Uma vez criado, um cursor de change stream permanece fixo em um nó mongod específico (primário ou secundário) e continua consumindo via getMore. Após eventos como troca de primário/secundário, redimensionamento de instância ou migração, um cursor originalmente em um secundário pode acabar no primário.

      Se você não quiser que essa carga persista no nó primário, limpe os cursores de change stream relevantes usando killCursors. Sua lógica de consumidor downstream pode então restaurar o cursor para um nó secundário usando o resumeToken e readPreference. Esta operação não causa perda ou interrupção de eventos.

      db.runCommand(
         {
           killCursors: <collection>,
           cursors: [ <cursor id1>, ... ], comment: <any>
         }
      )
      db.getSiblingDB("<testDB>").runCommand( { killCursors: "<testColl>", cursors: [NumberLong("2452840976689696187") ] } ) 

      4. Por que vejo muitos slow logs sobre change streams no mongos com duração de 1000 ms?

      2020-08-26T04:34:45.045+0000 I COMMAND [conn21283] command altconfig-b2b-perf.oplog command: getMore \{ getMore: 3513599116181216748, collection: "oplog", $db: "altconfig-b2b-perf", $clusterTime: { clusterTime: Timestamp(1598416483, 1), signature: { hash: BinData(0, EC3841EB1FB7A34F897688BB5983E32E2ADF6763), keyId: 6855385090000683010 } }, lsid: \{ id: UUID("72864d11-c0f8-48bb-823e-de5f18a9c409") } } originatingCommand: \{ find: "oplog", filter: { timestamp: { $gte: new Date(1597895390009) } }, tailable: true, awaitData: true, $db: "altconfig-b2b-perf", $clusterTime: \{ clusterTime: Timestamp(1597895487, 1), signature: { hash: BinData(0, AAAE33D5688F935C70469CBDB8EB1F6882749A40), keyId: 6855385090000683010 } }, lsid: \{ id: UUID("72864d11-c0f8-48bb-823e-de5f18a9c409") } } nShards:1 cursorid:3513599116181216748 numYields:0 nreturned:0 reslen:237 protocol:op_msg 1000ms

      Este é um comportamento normal em versões do MongoDB anteriores à 6.0. Indica um timeout de espera, não um gargalo de desempenho. Em arquiteturas de sharded cluster, o mongos cria cursores de change stream nos shards com tailable:true, awaitData:true e maxTimeMS:1000 para retornar o máximo possível de eventos de alteração dentro de 1000 ms.

      Nas versões principais anteriores à 6.0, o mongos registrava esses slow logs de 1000 ms, o que poderia enganar os usuários. O MongoDB otimizou esse comportamento. Para detalhes, consulte SERVER-50559.