Gerenciando Dados de Forma Eficiente com o Comando da API _bulk do Elasticsearch

Use a API _bulk do Elasticsearch corretamente com exemplos NDJSON, verificação de respostas, dimensionamento de lotes e orientações seguras de repetição.

Gerenciando Dados de Forma Eficiente com o Comando da API _bulk do Elasticsearch

A API _bulk do Elasticsearch é a ferramenta certa quando seu aplicativo precisa indexar, atualizar ou excluir muitos documentos sem pagar por uma solicitação HTTP por documento. A parte que confunde as pessoas é o corpo da solicitação: é JSON delimitado por nova linha, não uma matriz JSON bem formatada.

Use _bulk ao carregar logs, sincronizar registros de outro banco de dados ou aplicar um lote de exclusões de limpeza. Você ainda precisa inspecionar cada item na resposta, porque uma operação pode falhar enquanto a solicitação HTTP geral é bem-sucedida.

Entendendo a Estrutura da API _bulk

A API _bulk aceita JSON delimitado por nova linha, geralmente chamado de NDJSON. Cada ação é definida em uma linha. As ações que precisam de um corpo de documento usam a próxima linha como a carga útil de origem ou atualização. A linha final também deve terminar com uma nova linha.

Componentes Chave de uma Solicitação _bulk:

  • Linha de Ação e Metadados: Esta linha especifica o tipo de operação (index, create, update ou delete), o índice de destino e, opcionalmente, o ID do documento. Os tipos de documento não são usados nas APIs modernas do Elasticsearch.
  • Linha de Origem: Esta linha contém o documento JSON real a ser indexado ou atualizado. Esta linha é omitida para operações delete.
  • Delimitador de Nova Linha: Cada par ação/metadados e sua origem correspondente (se aplicável) devem ser separados por um caractere de nova linha (\n). Todo o corpo da solicitação deve terminar com um caractere de nova linha.

Exemplo de Estrutura:

{ "index": { "_index": "my-index", "_id": "1" } }
{ "field1": "value1" }
{ "delete": { "_index": "my-index", "_id": "2" } }

Ou para uma operação de exclusão:

curl -sS -H 'Content-Type: application/x-ndjson' \
  -X POST 'http://localhost:9200/_bulk' \
  --data-binary @bulk.ndjson

Realizando Operações Comuns com _bulk

A API _bulk é versátil e pode lidar com uma mistura de operações em uma única solicitação. É aqui que reside seu verdadeiro poder, permitindo realizar manipulações complexas de dados em uma única viagem de ida e volta.

Indexando Múltiplos Documentos

Para indexar vários documentos, você usa a ação index. Se um documento com o ID especificado já existir, index o substituirá. Se você quiser garantir que um documento seja indexado apenas se ainda não existir, use a ação create.

Exemplo: Indexando dois novos documentos.

{ "index": { "_index": "my-index", "_id": "1" } }
{ "field1": "value1", "field2": "value2" }
{ "index": { "_index": "my-index", "_id": "2" } }
{ "field1": "another_value", "field2": "different_value" }

Atualizando Documentos

A atualização de documentos pode ser feita usando a ação update. Você especifica o ID do documento a ser atualizado e fornece um documento parcial com os campos que deseja alterar. Se você quiser usar um script para atualização, pode fazê-lo dentro da ação update.

Exemplo: Atualizando um campo em um documento existente.

{ "update": { "_index": "my-index", "_id": "1" } }
{ "doc": { "field1": "updated_value" } }

Excluindo Documentos

Para excluir documentos, você usa a ação delete, especificando o _index e o _id do documento a ser removido. Nenhum documento de origem é necessário para operações de exclusão.

Exemplo: Excluindo um documento.

{ "delete": { "_index": "my-index", "_id": "2" } }

Combinando Operações

A verdadeira eficiência vem da mistura dessas operações. Você pode indexar novos documentos, atualizar os existentes e excluir outros, tudo na mesma solicitação _bulk.

Exemplo: Indexando, atualizando e excluindo em uma solicitação.

{ "index": { "_index": "my-index", "_id": "3" } }
{ "field1": "new_document_field", "field2": "new_document_value" }
{ "update": { "_index": "my-index", "_id": "1" } }
{ "doc": { "field1": "further_updated_value" } }
{ "delete": { "_index": "my-index", "_id": "2" } }

Manipulação de Respostas

A API _bulk retorna uma resposta JSON que detalha o resultado de cada operação individual. É crucial analisar esta resposta para verificar se todas as operações foram bem-sucedidas e identificar quaisquer erros.

A resposta conterá um array items, onde cada elemento corresponde a uma das operações em sua solicitação, na mesma ordem. Cada item incluirá a operação index, create, update ou delete, juntamente com seu status (por exemplo, created, updated, deleted, noop) e outros metadados relevantes.

Exemplo de Trecho de Resposta:

{
  "took": 150,
  "errors": false,
  "items": [
    {
      "index": {
        "_index": "my-index",
        "_id": "3",
        "version": 1,
        "result": "created",
        "_shards": {"total": 2, "successful": 1, "failed": 0},
        "_seq_no": 0,
        "_primary_term": 1
      }
    },
    {
      "update": {
        "_index": "my-index",
        "_id": "1",
        "version": 2,
        "result": "updated",
        "_shards": {"total": 2, "successful": 1, "failed": 0},
        "_seq_no": 1,
        "_primary_term": 1
      }
    },
    {
      "delete": {
        "_index": "my-index",
        "_id": "2",
        "version": 2,
        "result": "deleted",
        "_shards": {"total": 2, "successful": 1, "failed": 0},
        "_seq_no": 2,
        "_primary_term": 1
      }
    }
  ]
}

Se alguma operação falhar, o campo errors de nível superior na resposta será true, e o item individual para a operação com falha conterá um objeto error detalhando o problema.

Melhores Práticas e Dicas

  • Tamanho do Lote: Lotes muito grandes podem sobrecarregar a memória do cliente, nós coordenadores e nós de dados. Comece com cargas modestas, meça a taxa de transferência e as taxas de rejeição e ajuste para seu cluster e tamanho do documento.
  • Tratamento de Erros: Sempre analise a resposta em busca de erros. Implemente lógica de repetição para erros transitórios, se necessário.
  • Delimitadores de Nova Linha: Certifique-se de que os caracteres de nova linha (\n) sejam usados corretamente entre cada objeto JSON. A formatação incorreta é uma causa comum de falhas na API _bulk.
  • Paralelização: Para taxas de ingestão muito altas, considere enviar várias solicitações _bulk em paralelo, mas esteja atento à capacidade do seu cluster.
  • create vs. index: Use create quando quiser que a operação falhe se o ID já existir. Use index quando for aceitável substituir um documento existente.
  • Clientes de API: A maioria das bibliotecas de cliente do Elasticsearch fornece métodos convenientes para construir e executar solicitações _bulk, abstraindo parte da formatação manual.

Conclusão Prática

A API _bulk é rápida porque reduz a sobrecarga de solicitações, mas só é segura quando seu cliente trata a resposta como uma lista de resultados individuais. Envie NDJSON válido com Content-Type: application/x-ndjson, mantenha os lotes em um tamanho que seu cluster possa absorver e repita apenas as operações que falharam por motivos transitórios.