¿Cuál es la forma más rápida de escribir muchos documentos en Firestore?

Respuestas:

26

TL; DR: la forma más rápida de realizar la creación de fechas masivas en Firestore es realizar operaciones de escritura individuales paralelas.

Escribir 1,000 documentos en Firestore toma:

  1. ~105.4s cuando se utilizan operaciones de escritura individuales secuenciales
  2. ~ 2.8s cuando se utilizan (2) operaciones de escritura por lotes
  3. ~ 1.5s cuando se utilizan operaciones de escritura individuales paralelas

Hay tres formas comunes de realizar una gran cantidad de operaciones de escritura en Firestore.

  1. Realice cada operación de escritura individual en secuencia.
  2. Uso de operaciones de escritura por lotes.
  3. Realización de operaciones de escritura individuales en paralelo.

Investigaremos cada uno a continuación, utilizando una matriz de datos de documentos aleatorios.


Operaciones de escritura secuencial individual

Esta es la solución más simple posible:

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

Escribimos cada documento por turno, hasta que hayamos escrito todos los documentos. Y esperamos que se complete cada operación de escritura antes de comenzar con la siguiente.

Escribir 1,000 documentos toma aproximadamente 105 segundos con este enfoque, por lo que el rendimiento es de aproximadamente 10 documentos escritos por segundo .


Usar operaciones de escritura por lotes

Esta es la solución más compleja.

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;
    }
  }
}

Puedes ver que creamos un BatchedWriteobjeto llamandobatch() , llenándolo hasta su capacidad máxima de 500 documentos y luego escribiéndolo en Firestore. Le damos a cada documento un nombre generado que es relativamente probable que sea único (lo suficientemente bueno para esta prueba).

Escribir 1,000 documentos toma aproximadamente 2.8 segundos con este enfoque, por lo que el rendimiento es de aproximadamente 357 escrituras de documentos por segundo .

Eso es bastante más rápido que con las escrituras individuales secuenciales. De hecho: muchos desarrolladores usan este enfoque porque suponen que es más rápido, pero como los resultados anteriores ya mostraron, esto no es cierto. Y el código es, con mucho, el más complejo, debido a la restricción de tamaño en los lotes.


Operaciones de escritura individuales paralelas

La documentación de Firestore dice esto sobre el rendimiento para agregar muchos datos :

Para la entrada de datos en masa, use una biblioteca cliente de servidor con escrituras individuales paralelas. Las escrituras por lotes funcionan mejor que las escrituras serializadas pero no mejor que las escrituras paralelas.

Podemos poner eso a prueba con este código:

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

Este código inicia las addoperaciones lo más rápido que puede y luego Promise.all()espera a que finalicen. Con este enfoque, las operaciones pueden ejecutarse en paralelo.

Escribir 1,000 documentos toma aproximadamente 1.5 segundos con este enfoque, por lo que el rendimiento es de aproximadamente 667 escrituras de documentos por segundo .

La diferencia no es tan grande como entre los dos primeros enfoques, pero sigue siendo 1,8 veces más rápida que las escrituras por lotes.


Algunas notas

  • Puede encontrar el código completo de esta prueba en Github .
  • Si bien la prueba se realizó con Node.js, es probable que obtenga resultados similares en todas las plataformas que admite el SDK de administración.
  • Sin embargo, no realice inserciones masivas con SDK de clientes, ya que los resultados pueden ser muy diferentes y mucho menos predecibles.
  • Como de costumbre, el rendimiento real depende de su máquina, el ancho de banda y la latencia de su conexión a Internet, y muchos otros factores. De acuerdo con ellos, también puede ver diferencias en las diferencias, aunque espero que el pedido siga siendo el mismo.
  • Si tiene valores atípicos en sus propias pruebas o encuentra resultados completamente diferentes, deje un comentario a continuación.
  • Los lotes escritos son atómicos. Por lo tanto, si tiene dependencias entre los documentos y todos los documentos deben estar escritos, o ninguno de ellos debe estar escrito, debe usar una escritura por lotes.
Frank van Puffelen
fuente
1
Esto es súper interesante, ¡gracias por hacer el trabajo! OOC, ¿probaste ejecutar las escrituras por lotes en paralelo? Obviamente, en ese caso, deberá estar aún más seguro para evitar que cualquier documento se encuentre en ambos lotes.
robsiemb
1
Estaba a punto de probar escrituras paralelas en lotes, pero se me acabó la cuota (es un proyecto gratuito y era demasiado flojo para actualizar). Hoy es otro día, así que podría intentarlo y actualizar mi respuesta si es significativo.
Frank van Puffelen
2
@robsiemb También probé con escrituras paralelas por lotes también. El rendimiento es muy similar a las escrituras paralelas individuales, por lo que diría que están empatados en primer lugar en mis pruebas. Espero que las escrituras en lotes se deterioren más rápido debido a la naturaleza que se procesan en el back-end. Combinado con el código mucho más complejo, aún recomendaría usarlos solo por su atomicidad y no por la ventaja de rendimiento percibida pero inexistente.
Frank van Puffelen
@FrankvanPuffelen las escrituras paralelas serán más rápidas también si "configuro" documentos en lugar de "agregar" documentos. Quiero decir, db.collection ('ciudades'). Doc ('LA'). Set (data) en lugar de db.collection ('cities'). Add (data)
alek6dj
Las llamadas add()no hacen más que generar una ID única (puramente del lado del cliente), seguida de una set()operación. Entonces los resultados deberían ser los mismos. Si eso no es lo que observa, publique una nueva pregunta con el caso mínimo que reproduce lo que ha intentado.
Frank van Puffelen