Какой самый быстрый способ написать много документов в Firestore?


Ответы:


26

TL; DR. Самый быстрый способ выполнить массовое создание даты в Firestore - это выполнить параллельные отдельные операции записи.

Запись 1000 документов в Firestore занимает:

  1. ~105.4s при использовании последовательных отдельных операций записи
  2. ~ 2.8s при использовании (2) операций пакетной записи
  3. ~ 1.5s при использовании параллельных отдельных операций записи

Существует три распространенных способа выполнения большого количества операций записи в Firestore.

  1. Выполните каждую отдельную операцию записи в последовательности.
  2. Использование пакетных операций записи.
  3. Выполнение отдельных операций записи параллельно.

Мы рассмотрим каждый из них по очереди, используя массив случайных данных документа.


Индивидуальные последовательные операции записи

Это самое простое из возможных решений:

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

Мы пишем каждый документ по очереди, пока не напишем каждый документ. И мы ждем завершения каждой операции записи, прежде чем начинать следующую.

При таком подходе запись 1000 документов занимает около 105 секунд, поэтому пропускная способность составляет примерно 10 операций записи документов в секунду .


Использование пакетных операций записи

Это самое сложное решение.

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

Вы можете видеть, что мы создаем BatchedWriteобъект путем вызова batch(), заполняем его до максимальной вместимости 500 документов, а затем записываем его в Firestore. Мы даем каждому документу сгенерированное имя, которое, скорее всего, будет уникальным (достаточно для этого теста).

При таком подходе запись 1000 документов занимает около 2,8 секунды, поэтому пропускная способность составляет примерно 357 операций записи документов в секунду .

Это немного быстрее, чем с последовательными отдельными записями. На самом деле: многие разработчики используют этот подход, потому что считают, что он самый быстрый, но, как уже показали результаты, это не так. И код на сегодняшний день является наиболее сложным из-за ограничения размера пакетов.


Параллельные индивидуальные операции записи

Документация Firestore говорит о производительности для добавления большого количества данных :

Для массового ввода данных используйте серверную клиентскую библиотеку с распараллеленными отдельными записями. Пакетные записи работают лучше, чем сериализованные записи, но не лучше, чем параллельные записи.

Мы можем проверить это с помощью этого кода:

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

Этот код запускает addоперации так быстро, как может, а затем использует, Promise.all()чтобы дождаться их завершения. При таком подходе операции могут выполняться параллельно.

При таком подходе запись 1000 документов занимает около 1,5 секунд, поэтому пропускная способность составляет примерно 667 операций записи документов в секунду .

Разница не так велика, как между первыми двумя подходами, но все же в 1,8 раза быстрее, чем пакетная запись.


Несколько заметок:

  • Вы можете найти полный код этого теста на Github .
  • Несмотря на то, что тест проводился с Node.js, вы, вероятно, получите схожие результаты на всех платформах, которые поддерживает Admin SDK.
  • Не выполняйте массовую вставку с использованием клиентских SDK, поскольку результаты могут быть очень разными и гораздо менее предсказуемыми.
  • Как обычно, фактическая производительность зависит от вашей машины, пропускной способности и задержки вашего интернет-соединения, а также от многих других факторов. Исходя из этого, вы также можете увидеть различия в различиях, хотя я ожидаю, что порядок останется прежним.
  • Если у вас есть какие-либо отклонения в ваших собственных тестах, или вы нашли совершенно другие результаты, оставьте комментарий ниже.
  • Партии пишет атомные. Таким образом, если у вас есть зависимости между документами, и все документы должны быть написаны, или ни один из них не должен быть написан, вы должны использовать пакетную запись.

1
Это супер интересно, спасибо за работу! ООК, вы тестировали параллельное выполнение пакетной записи? Очевидно, что в этом случае вам нужно быть еще более уверенным, чтобы избежать попадания любого документа в обе партии.
robsiemb

1
Я собирался протестировать параллельные пакетные записи, но у меня закончилась квота (это бесплатный проект, и мне было лень обновляться). Сегодня еще один день, поэтому я могу попробовать и обновить свой ответ, если он значительный.
Фрэнк ван Пуффелен

2
@robsiemb Я только что проверил с параллельными пакетными записями тоже. Производительность очень похожа на отдельные параллельные записи, так что я бы сказал, что они были первыми в моих тестах. Я ожидаю, что пакетные записи могут ухудшаться быстрее из-за характера, который они обрабатывают на бэкэнде. В сочетании с гораздо более сложным кодом я бы порекомендовал использовать их только для атомарности, а не для ощутимого, но несуществующего преимущества в производительности.
Фрэнк ван Пуффелен

@FrankvanPuffelen параллельные записи будут быстрее, если я «установлю» документы вместо «добавлю» документы? Я имею в виду db.collection («города»). Doc («ЛА»). Набор (данные) вместо db.collection («города»). Add (данные)
alek6dj

Вызов add()не делает ничего, кроме генерации уникального идентификатора (чисто на стороне клиента), за которым следует set()операция. Так что результаты должны быть одинаковыми. Если это не то, что вы наблюдаете, опубликуйте новый вопрос с минимальным регистром, который воспроизводит то, что вы пробовали.
Фрэнк ван Пуффелен
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.