Qual é a maneira mais rápida de escrever muitos documentos no Firestore?

11

Preciso escrever um grande número de documentos no Firestore.

Qual é a maneira mais rápida de fazer isso no Node.js?

Frank van Puffelen
fonte

Respostas:

26

TL; DR: A maneira mais rápida de executar a criação de datas em massa no Firestore é executando operações de gravação individuais paralelas.

Escrever 1.000 documentos no Firestore leva:

  1. ~105.4s ao usar operações de gravação individual sequencial
  2. ~ 2.8s ao usar (2) operações de gravação em lote
  3. ~ 1.5s ao usar operações de gravação individuais paralelas

Existem três maneiras comuns de executar um grande número de operações de gravação no Firestore.

  1. Execute cada operação de gravação individual em sequência.
  2. Usando operações de gravação em lote.
  3. Executando operações de gravação individuais em paralelo.

Investigaremos cada um deles por vez, usando uma matriz de dados de documentos aleatórios.


Operações de gravação sequencial individual

Esta é a solução mais simples possível:

async function testSequentialIndividualWrites(datas) {
  while (datas.length) {
    await collection.add(datas.shift());
  }
}

Escrevemos cada documento por vez, até escrevermos todos os documentos. E aguardamos a conclusão de cada operação de gravação antes de iniciar a próxima.

A gravação de 1.000 documentos leva cerca de 105 segundos com essa abordagem; portanto, a taxa de transferência é de aproximadamente 10 gravações de documentos por segundo .


Usando operações de gravação em lote

Esta é a solução mais complexa.

async function testBatchedWrites(datas) {
  let batch = admin.firestore().batch();
  let count = 0;
  while (datas.length) {
    batch.set(collection.doc(Math.random().toString(36).substring(2, 15)), datas.shift());
    if (++count >= 500 || !datas.length) {
      await batch.commit();
      batch = admin.firestore().batch();
      count = 0;
    }
  }
}

Você pode ver que criamos um BatchedWriteobjeto chamando batch(), preencha-o até sua capacidade máxima de 500 documentos e, em seguida, grave-o no Firestore. Atribuímos a cada documento um nome gerado que é relativamente provável que seja único (bom o suficiente para este teste).

A gravação de 1.000 documentos leva cerca de 2,8 segundos com essa abordagem; portanto, a taxa de transferência é de aproximadamente 357 gravações de documentos por segundo .

Isso é um pouco mais rápido do que com as gravações individuais sequenciais. De fato: muitos desenvolvedores usam essa abordagem porque assumem que é a mais rápida, mas como os resultados acima já mostraram isso não é verdade. E o código é de longe o mais complexo, devido à restrição de tamanho nos lotes.


Operações de gravação individuais paralelas

A documentação do Firestore diz isso sobre o desempenho da adição de muitos dados :

Para entrada de dados em massa, use uma biblioteca-cliente do servidor com gravações individuais paralelas. Gravações em lote têm melhor desempenho do que gravações serializadas, mas não melhor que gravações paralelas.

Podemos colocar isso à prova com este código:

async function testParallelIndividualWrites(datas) {
  await Promise.all(datas.map((data) => collection.add(data)));
}

Esse código inicia as addoperações o mais rápido possível e, em seguida, Promise.all()aguarda até que todas estejam concluídas. Com essa abordagem, as operações podem ser executadas em paralelo.

A gravação de 1.000 documentos leva cerca de 1,5 segundos com essa abordagem; portanto, o rendimento é de aproximadamente 667 gravações de documentos por segundo .

A diferença não é tão grande quanto entre as duas primeiras abordagens, mas ainda é 1,8 vezes mais rápida que as gravações em lote.


Algumas notas:

  • Você pode encontrar o código completo deste teste no Github .
  • Enquanto o teste foi realizado com o Node.js, é provável que você obtenha resultados semelhantes em todas as plataformas suportadas pelo SDK do administrador.
  • Porém, não faça inserções em massa usando SDKs de clientes, pois os resultados podem ser muito diferentes e muito menos previsíveis.
  • Como de costume, o desempenho real depende da sua máquina, da largura de banda e da latência da sua conexão à Internet e de muitos outros fatores. Com base nesses, você também pode ver diferenças nas diferenças, embora eu espere que a ordem permaneça a mesma.
  • Se você tiver discrepantes em seus próprios testes ou encontrar resultados completamente diferentes, deixe um comentário abaixo.
  • As gravações em lote são atômicas. Portanto, se você tiver dependências entre os documentos e todos os documentos tiverem que ser escritos, ou nenhum deles tiver que ser gravado, use uma gravação em lote.
Frank van Puffelen
fonte
11
Isso é super interessante, obrigado por fazer o trabalho! OOC, você testou a execução das gravações em lote em paralelo? Obviamente, nesse caso, você precisaria ter ainda mais certeza de evitar que qualquer documento esteja nos dois lotes.
22419 robsiemb #
11
Eu estava prestes a testar gravações em lote paralelas, mas ficou sem cota (é um projeto gratuito e estava com preguiça de atualizar). Hoje é outro dia, então eu posso tentar e atualizar minha resposta, se for significativa.
precisa
2
@robsiemb Acabei de testar também com gravações em lote paralelas. O desempenho é muito semelhante às gravações paralelas individuais, então eu diria que elas estão empatadas pela primeira vez em meus testes. Espero que as gravações em lote se deteriorem mais rapidamente devido à natureza em que são processadas no back-end. Combinado com o código muito mais complexo, eu ainda recomendaria usá-lo apenas pela atomicidade e não pela vantagem de desempenho percebida, mas inexistente.
Frank van Puffelen
As gravações paralelas do @FrankvanPuffelen também serão mais rápidas se eu "definir" documentos em vez de "adicionar" documentos? Quero dizer, db.collection ('cities'). Doc ('LA'). Set (data) em vez de db.collection ('cities'). Add (data)
alek6dj
Chamar add()nada mais do que gerar um ID exclusivo (puramente do lado do cliente), seguido por uma set()operação. Portanto, os resultados devem ser os mesmos. Se não é o que você observa, poste uma nova pergunta com o mínimo de casos que reproduz o que você tentou.
Frank van Puffelen 23/04