Який найшвидший спосіб написати багато документів у 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
Це супер цікаво, дякую за виконану роботу! OOC, ви тестували паралельне написання пакетних записів? Очевидно, що в цьому випадку вам потрібно бути ще впевненішими, щоб уникнути будь-якого документа в обох партіях.
robsiemb

1
Я збирався тестувати паралельне написання записів, але не вистачало квоти (це безкоштовний проект, і я лінувався модернізувати). Сьогодні ще один день, тому я можу спробувати і оновити свою відповідь, якщо вона важлива.
Франк ван

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

@FrankvanPuffelen паралельне записування буде швидше, якщо я також "встановлю" документи замість "додати" документи? Я маю на увазі, db.collection ('міст'). Doc ('LA'). Встановити (дані) замість db.collection ('міст'). Add (data)
alek6dj

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