Проблема повільної продуктивності Firestore при отриманні даних


78

У мене виникають проблеми з повільною продуктивністю Firestore під час отримання основних даних, що зберігаються в документі, порівняно з базою даних у реальному часі зі співвідношенням 1/10.

Використання Firestore займає в середньому 3000 мс на перший дзвінок

 this.db.collection(‘testCol’)
   .doc(‘testDoc’)
   .valueChanges().forEach((data) => {
     console.log(data);//3000 ms later
 });

Використовуючи базу даних у режимі реального часу, на перший дзвінок потрібно в середньому 300 мс

 this.db.database.ref(‘/test’).once(‘value’).then(data => {
     console.log(data); //300ms later
 });

Це знімок екрана мережевої консолі:

Проблема повільної продуктивності Firestore отримує дані

Я використовую Javascript SDK v4.50 з AngularFire2 v5.0 rc.2.

Хтось стикався з цією проблемою?


Яку ефективність ви бачите, якщо зробите другий дзвінок (до іншого документа / колекції)? Чи бачите ви ту саму проблему, якщо не використовуєте angularfire?
Сем Стерн

Я маю подібний досвід. Перший дзвінок трохи повільний, іноді 5-10 секунд. Я роблю програму для чату - перша повідомлення доставляється трохи часу, проте наступні майже миттєві. Firestore все ще є бета-версією, вони, ймовірно, все ще розбираються химерності.
xenetics

1
Подібний досвід тут. First onSnapShot займає надзвичайно багато часу - до 2 хв для деяких користувачів, що робить наш додаток невикористовуваним
wizloc

Та сама проблема, досить неприємна. Деякі повідомляють, що написання звільнить "завислі" запити.
DarkNeuron

Те саме видання, до 1,5 хв за допомогою простого збору collection.get (документа)
aMarCruz

Відповіді:


44

ОНОВЛЕННЯ: 12 лютого 2018 року - SDK iOS Firestore v0.10.0

Подібно до деяких інших коментаторів, я також помітив повільнішу реакцію на перший запит отримання (з наступними запитами, які займають ~ 100 мс). Для мене це не так погано, як 30-ті роки, але, можливо, приблизно 2-3 секунди, коли я маю хороший зв’язок, цього достатньо, щоб забезпечити поганий досвід роботи користувача при запуску мого додатка.

Firebase повідомили, що їм відомо про цю проблему "холодного старту" і вони працюють над довгостроковим виправленням - на жаль, немає часу прибуття. Я думаю, що це окреме питання, що коли у мене поганий зв’язок, може знадобитися вік (понад 30 років), перш ніж запити приймуть рішення про читання з кешу.

Поки Firebase вирішує всі ці проблеми, я почав використовувати нове disableNetwork() та enableNetwork()методи (доступні у Firestore v0.10.0) для ручного контролю стану Інтернету / офлайн у Firebase. Хоча мені довелося бути дуже обережним, де я використовую його у своєму коді, оскільки існує помилка Firestore, яка може спричинити збій за певних сценаріїв.


ОНОВЛЕННЯ: 15 листопада 2017 р. - SDK iOS Firestore v0.9.2

Здається, проблему з повільною продуктивністю виправлено. Я повторно запустив тести, описані нижче, і час, який потрібен Firestore, щоб повернути 100 документів, зараз здається постійно близько 100 мс.

Не впевнений, чи це було виправлення в останньому SDK v0.9.2, чи це виправлення внутрішньої мережі (або в обох випадках), але я пропоную всім оновити свої підрозділи Firebase. Мій додаток значно помітніший - подібний до того, як це було в БД реального часу.


Я також виявив, що Firestore працює набагато повільніше, ніж БД у реальному часі, особливо під час читання з безлічі документів.

Оновлені тести (з останньою iOS Firestore SDK v0.9.0):

Я створив тестовий проект в iOS Swift, використовуючи як RTDB, так і Firestore, і провів по 100 послідовних операцій читання на кожному. Що стосується RTDB, я протестував метод obserSingleEvent і методи спостереження на кожному з 100 вузлів верхнього рівня. Для Firestore я використовував методи getDocument та addSnapshotListener у кожному із 100 документів у колекції TestCol. Я провів тести, постійно вмикаючи та вимикаючи диск. Будь ласка, зверніться до вкладеного зображення, де показано структуру даних для кожної бази даних.

Я провів тест 10 разів для кожної бази даних на одному пристрої та стабільній мережі Wi-Fi. Існуючих спостерігачів та слухачів знищували перед кожним новим прогоном.

Метод БД у режимі реального часу спостерігатиSingleEvent:

func rtdbObserveSingle() {

    let start = UInt64(floor(Date().timeIntervalSince1970 * 1000))
    print("Started reading from RTDB at: \(start)")

    for i in 1...100 {
        Database.database().reference().child(String(i)).observeSingleEvent(of: .value) { snapshot in
            let time = UInt64(floor(Date().timeIntervalSince1970 * 1000))
            let data = snapshot.value as? [String: String] ?? [:]
            print("Data: \(data). Returned at: \(time)")
        }
    }
}

Метод спостереження БД у реальному часі:

func rtdbObserve() {

    let start = UInt64(floor(Date().timeIntervalSince1970 * 1000))
    print("Started reading from RTDB at: \(start)")

    for i in 1...100 {
        Database.database().reference().child(String(i)).observe(.value) { snapshot in
            let time = UInt64(floor(Date().timeIntervalSince1970 * 1000))
            let data = snapshot.value as? [String: String] ?? [:]
            print("Data: \(data). Returned at: \(time)")
        }
    }
}

Метод getDocument Firestore:

func fsGetDocument() {

    let start = UInt64(floor(Date().timeIntervalSince1970 * 1000))
    print("Started reading from FS at: \(start)")

    for i in 1...100 {
        Firestore.firestore().collection("TestCol").document(String(i)).getDocument() { document, error in

            let time = UInt64(floor(Date().timeIntervalSince1970 * 1000))
            guard let document = document, document.exists && error == nil else {
                print("Error: \(error?.localizedDescription ?? "nil"). Returned at: \(time)")
                return
            }
            let data = document.data() as? [String: String] ?? [:]
            print("Data: \(data). Returned at: \(time)")
        }
    }
}

Метод addSnapshotListener Firestore:

func fsAddSnapshotListener() {

    let start = UInt64(floor(Date().timeIntervalSince1970 * 1000))
    print("Started reading from FS at: \(start)")

    for i in 1...100 {
        Firestore.firestore().collection("TestCol").document(String(i)).addSnapshotListener() { document, error in

            let time = UInt64(floor(Date().timeIntervalSince1970 * 1000))
            guard let document = document, document.exists && error == nil else {
                print("Error: \(error?.localizedDescription ?? "nil"). Returned at: \(time)")
                return
            }
            let data = document.data() as? [String: String] ?? [:]
            print("Data: \(data). Returned at: \(time)")
        }
    }
}

Кожен метод по суті друкує мітку часу unix у мілісекундах, коли метод починає виконуватися, а потім друкує іншу мітку часу unix, коли повертається кожна операція читання. Я взяв різницю між початковою міткою часу та останньою міткою часу, щоб повернутися.

РЕЗУЛЬТАТИ - Вимкнено стійкість диска:

Стійкість диска відключена

РЕЗУЛЬТАТИ - Увімкнено стійкість диска:

Увімкнено збереження диска

Структура даних:

Структура даних

Коли способи Firestore getDocument / addSnapshotListener застряють, здається, вони застрягли на тривалість, яка приблизно дорівнює 30 секундам. Можливо, це може допомогти команді Firebase виділити, де в SDK вона застряє?


2
Отже, firestore дорожчий і набагато повільніший .. Сподіваюся, команда firebase побачить це
faruk

6
[Firebaser тут] дякуємо, що знайшли час надати такі докладні дані, ми завжди це вдячні. Справа не в тому, що система „повільніша”, а в тому, що дуже мала кількість запитів застряє або вимагає величезної кількості часу для повернення. Незабаром у нас є деякі виправлення, які, на нашу думку, покращать ситуацію.
Сем Стерн,

1
Дякуємо, що постійно нас оновлювали. Я додав кілька нових результатів для останньої версії Firestore SDK v0.9.0, яка може допомогти вашій команді виявити джерело проблеми. Також у мене виникла ще одна проблема із прослуховувачем знімків: stackoverflow.com/questions/46710371/ ... Основна причина може бути пов’язана з цією темою, а може не пов’язана, але було б чудово, якби команда Firebase могла поглянути на неї . Дуже дякую!
Саул

3
Ми також відчуваємо "застряглі" запити в Інтернеті sdk. Затримується на 10-20 секунд, потім надходять дані (на v4.8.0).
DarkNeuron

1
Я це нещодавно помітив і повідомив Firebase. Вони знають про проблему "холодного старту" і працюють над виправленням. Тим часом я пробую обхідний шлях, детально описаний у моєму оновленні вище, і мав з ним різний ступінь успіху.
Саул

21

Дата оновлення 02 березня 2018 р

Схоже, це відома проблема, і інженери Firestore працюють над виправленням. Після кількох обмінів електронною поштою та обміну кодом з інженером Firestore щодо цього питання, це була його відповідь на сьогодні.

"Ви насправді маєте рацію. При подальшій перевірці ця повільність API getDocuments () є відомою поведінкою в бета-версії Cloud Firestore. Наші інженери знають про цю проблему продуктивності, позначену як" холодний запуск ", але не хвилюйтеся, як ми це робимо наше найкраще для покращення продуктивності запитів Firestore.

Ми вже працюємо над довгостроковим виправленням, але наразі я не можу поділитися ніякими термінами чи специфікою. Поки Firestore все ще перебуває у бета-версії, очікуйте, що в майбутньому буде ще більше вдосконалень ".

Тож сподіваємось, це незабаром буде вибито.


Використання Swift / iOS

Після розв’язання цього питання протягом приблизно 3 днів здається, що проблема, безумовно, полягає у get (), тобто .getDocuments та .getDocument. Те, що, на мою думку , спричиняло надзвичайні, але періодичні затримки, але, здається, це не так:

  1. Не настільки чудове підключення до мережі
  2. Повторні дзвінки за допомогою циклу .getDocument ()
  3. Мережеві дзвінки get ()
  4. Firestore Холодний запуск
  5. Отримання кількох документів (Отримання 1 невеликого документа спричинило затримки 20 секунд)
  6. Кешування (я відключив постійну роботу в автономному режимі, але це нічого не зробило.)

Я зміг виключити все це, оскільки помітив, що ця проблема не траплялася при кожному дзвінку до бази даних Firestore, який я робив. Тільки отримання за допомогою get (). Для ударів я замінив .getDocument на .addSnapshotListener, щоб отримати мої дані та вуаля. Миттєве отримання кожного разу, включаючи перший дзвінок. Ніяких холодних стартів. Поки що проблем із .addSnapshotListener немає, лише getDocument (s).

Наразі я просто кидаю .getDocument () там, де важливий час, і замінюю його на .addSnapshotListener, а потім використовую

for document in querySnapshot!.documents{
// do some magical unicorn stuff here with my document.data()
}

... для того, щоб продовжувати рухатися, поки це не буде розроблено Firestore.


Я також бачу таку ж поведінку, але лише в Android. Наразі я також повертаюся до знімків. Але буде добре, якщо ефективність запитів get буде послідовною.
sowdri

Я також бачу низьку продуктивність, а також адаптер рециркулятора FirebaseUI, який використовує addSnapshotListener.
Джефф Паджетт,

Чи існує ця проблема "холодного старту" все ще? Мене трохи бентежить ваше оновлення від березня, в якому згадується, що інженери Firebase усвідомлюють те, що вони відзначають як проблему "холодного старту", тому що у своїй оригінальній відповіді ви написали, що ви виключили "4. Firestore Cold start". проблема?
троллкоце

Я все ще спостерігаю повільну продуктивність в Android і багато проблем з пам'яттю. Ви, хлопці, плануєте надати будь-яке оновлення з цим виправленням
hiten pannu

Проблема все ще трапляється з останньою версією firestore i. iOS та використання прослуховувача знімків працювали як шарм. Чудова знахідка.
johndoe

8

У мене було це питання до цього ранку. Запит мого Firestore через iOS / Swift займе близько 20 секунд, щоб виконати простий, повністю проіндексований запит - із непропорційним часом запиту для 1 повернутого товару - аж до 3000.

Моє рішення було відключити збереження даних в автономному режимі. У моєму випадку це не відповідало потребам нашої бази даних Firestore, яка щодня оновлює великі частини своїх даних.

Для користувачів iOS та Android ця опція ввімкнена за замовчуванням, тоді як для веб-користувачів вона вимкнена за замовчуванням. Це робить Firestore здається шалено повільним, якщо ви запитуєте величезну колекцію документів. В основному він кешує копію будь-яких даних, які ви запитуєте (і яку б колекцію ви не запитували - я вважаю, що вона кешує всі документи всередині), що може призвести до великого використання пам'яті.

У моєму випадку це спричинило величезне очікування кожного запиту, поки пристрій не кешує необхідні дані, - отже, непропорційний час запитів для збільшення кількості елементів, що повертаються з тієї самої колекції. Це пов’язано з тим, що для кешування колекції в кожному запиті знадобилася однакова кількість часу.

Дані в режимі офлайн - із Документів Cloud Firestore

Я провів тестування для відображення цього ефекту (з увімкненою автономною стійкістю) з тієї самої колекції, що запитується, але з різною кількістю елементів, повернутих за допомогою параметра .limit:

Тести Тепер, коли повернуто 100 елементів (з відключеною постійною активністю), для мого запиту потрібно менше 1 секунди.

Мой код запиту Firestore знаходиться нижче:

let db = Firestore.firestore()
self.date = Date()
let ref = db.collection("collection").whereField("Int", isEqualTo: SomeInt).order(by: "AnotherInt", descending: true).limit(to: 100)
ref.getDocuments() { (querySnapshot, err) in
    if let err = err {
        print("Error getting documents: \(err)")
    } else {
        for document in querySnapshot!.documents {
            let data = document.data()
            //Do things
        }
        print("QUERY DONE")
        let currentTime = Date()
        let components = Calendar.current.dateComponents([.second], from: self.date, to: currentTime)
        let seconds = components.second!
        print("Elapsed time for Firestore query -> \(seconds)s")
        // Benchmark result
    }
}

Це тестові номери з Інтернету, iOS або Android?
Сем Стерн,

Орієнтир - від iOS, хоча я очікував би підвищення продуктивності на всіх платформах - залежно від розміру вашої колекції, що запитується
Hendies

Не підозрював, що ти в команді Firebase! Віддача цього запиту полягала в тому, що використання пам’яті зростало б до великих цифр (600-700 Мб оперативної пам'яті), де я вважав, що наша колекція зберігається в кеші. Запит завжди зависає, а потім завершується, як тільки пам'ять поступово збільшується, а потім досягає тієї ж точки (700 Мб-іш). Після того, як стійкість була відключена, цей ефект припинився, і наша пам’ять залишилася, як очікувалось (100-150 Мб), при цьому повернувши наші результати надшвидко. Якщо вам потрібна додаткова інформація, будь ласка, запитайте.
Hendies

Га, це дуже цікаво. Не могли б ви створити зразок проекту Xcode, який копіює цей файл, і надіслати його мені електронною поштою? Якщо так, то це samstern на Google dot com. Я хотів би придивитися уважніше.
Сем Стерн

@SamStern: це було давно, але я стикаюся з точно такою ж проблемою на Android. Будь-які підказки щодо того, що може спричинити це? Я намагався створити мінімальний проект, але цей проект не має цієї проблеми!
rednuht

6

Майже через 3 роки firestore вже вийшов з бета-версії, і я можу підтвердити, що ця жахлива проблема все ще зберігається ;-(

У нашому мобільному додатку ми використовуємо клієнт javascript / node.js Firebase. Після численних тестувань, щоб з’ясувати, чому час запуску нашої програми становить близько 10 секунд, ми визначили, що віднести 70% цього часу до ... Ну, до проблем продуктивності Firebase та Firestore та холодного запуску:

  • firebase.auth (). onAuthStateChanged () запускає прибл. через 1,5 - 2 сек, вже зовсім погано.
  • Якщо він повертає користувача, ми використовуємо його ідентифікатор, щоб отримати документ користувача з firestore. Це перший виклик до firestore, і відповідний get () займає 4 - 5 сек. Подальше отримання () того самого чи інших документів займає приблизно 500 мс.

Отже, загалом ініціалізація користувача триває 6 - 7 секунд, абсолютно неприпустимо. І ми нічого з цим не можемо зробити. Ми не можемо перевірити відключення постійності, оскільки в клієнті javascript такої опції немає, постійність завжди ввімкнена за замовчуванням, тому не викликаючи enablePersistence () нічого не змінить.


Будь-яке вирішення цього питання? Будемо вдячні деяким підказкам, де шукати відповіді.
bjornl

У клієнті javascript він вимкнений за замовчуванням: For the web, offline persistence is disabled by default. To enable persistence, call the enablePersistence methodале я можу підтвердити, коли його вимкнено, час нашого початкового запиту переходить приблизно з приблизно 8 секунд до приблизно ~ 500 мс firebase.google.com/docs/firestore/manage-data/ enable-offline
погладити

Правильно, у javascript він вимкнений за замовчуванням, і час, про який я згадував вище, має значення за замовчуванням. І в нашому випадку нам потрібні свіжі та оновлені дані профілю користувача, тому використання постійності не є варіантом.
JPJ

1

ну, з того, що я зараз роблю та досліджую, використовуючи nexus 5X в емуляторі та справжньому андроїд-телефоні Huawei P8,

Firestore та хмарне сховище дають мені головний біль повільної реакції, коли я роблю перший document.get () та перший storage.getDownloadUrl ()

Це дає мені більше 60 секунд відповіді на кожен запит. Повільна реакція відбувається лише в реальному телефоні Android. Не в емуляторі. Ще одна дивна річ. Після першої зустрічі запит на відпочинок є гладким.

Ось простий код, де я зустрічаю повільну реакцію.

var dbuserref = dbFireStore.collection('user').where('email','==',email);
const querySnapshot = await dbuserref.get();

var url = await defaultStorage.ref(document.data().image_path).getDownloadURL();

Я також знайшов посилання, яке досліджує те саме. https://reformatcode.com/code/android/firestore-document-get-performance

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.