Firebase Realtime Database

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 19:12, 4 июня 2020.

База данных Firebase Realtime - это облачная NoSQL база данных компании Firebase. Данные хранятся в формате JSON и синхронизируются в режиме реального времени с каждым подключенным клиентом. Поддержаны особенности интеграции с приложениями под операционные системы Android и iOS, реализовано API для приложений на JavaScript, Java, Objective-C и Node.js, также возможно работать напрямую с базой данных в стиле REST из ряда JavaScript-фреймворков, включая AngularJS, React, Vue.js, Ember.js и Backbone.js. Предусмотрено API для шифрования данных. При создании кроссплатформенных приложения с помощью SDK для iOS, Android и JavaScript, все клиенты совместно используют один экземпляр базы данных в реальном времени и автоматически получают обновления с самыми новыми данными. [Источник 1].

Firebase Realtime Database
Firebase Logo.png
Создатели: Джеймс Тэмплин, Эндрю Ли
Разработчики: Google
Выпущена: 2011; 10 years ago (2011)
Состояние разработки: Завершена
Операционная система: Linux, OS X, Windows,Android, IOS
Локализация: English
Тип ПО: СУБД
Веб-сайт firebase.google.com

Основная информация

Ключевые особенности

Работа в реальном времени
Вместо типичных HTTP-запросов база данных Firebase Realtime использует синхронизацию данных - каждый раз, когда данные меняются, любое подключенное устройство получает это обновление в течение миллисекунд. Что позволяет обеспечивать совместный и захватывающий опыт, не думая о сетевом коде.
Оффлайн работа
Приложения Firebase остаются отзывчивыми даже в автономном режиме, поскольку SDK базы данных Firebase Realtime сохраняет ваши данные на диск. После восстановления соединения клиентское устройство получает любые пропущенные изменения, синхронизируя его с текущим состоянием сервера.
Доступность с устройств клиентов
Доступ к базе данных Firebase Realtime можно получить непосредственно с мобильного устройства или веб-браузера; нет необходимости в сервере приложений. Безопасность и проверка данных доступны через правила безопасности баз данных Firebase Realtime, основанные на выражениях правила, которые выполняются при чтении или записи данных.
Масштабирование по нескольким базам данных
С помощью базы данных Firebase Realtime можно масштабировать потребности приложений в данных, разбивая данные на несколько экземпляров базы данных в одном проекте Firebase. Есть возможность оптимизировать аутентификацию с помощью Firebase Authentication для вашего проекта и проверять подлинность пользователей во всех экземплярах базы данных. Управлять доступом к данным в каждой базе данных с помощью пользовательских правил базы данных Firebase Realtime для каждого экземпляра базы данных.

Применение

База данных Firebase Realtime позволяет создавать многофункциональные приложения для совместной работы, обеспечивая безопасный доступ к базе данных непосредственно из кода на стороне клиента. Данные сохраняются локально, и даже в автономном режиме события в реальном времени продолжают срабатывать, предоставляя пользователю полноценный отклик. Когда устройство восстанавливает соединение, база данных реального времени синхронизирует локальные изменения данных с удаленными обновлениями, которые произошли, когда клиент находился в автономном режиме, автоматически объединяя любые изменения.

База данных реального времени предоставляет гибкий язык правил на основе выражений, называемый правилами безопасности баз данных в реальном времени Firebase, для определения того, как ваши данные должны быть структурированы и когда данные могут считываться или записываться. При интеграции с Аутентификацией Firebase разработчики могут определить, кто имеет доступ к каким данным и как они могут получить к ним доступ.

База данных реального времени является базой данных NoSQL и, поэтому имеет иные виды оптимизации и функциональность по сравнению с реляционной базой данных. API-интерфейс базы данных реального времени позволяет выполнять только те операции, которые могут быть выполнены быстро. Это позволяет вам производить обработку данных в реальном времени, обслуживая миллионы пользователей без ущерба для скорости отклика. В связи с этим важно продумать то, как пользователи должны получать доступ к вашим данным, и затем соответствующим образом структурировать их .
[Источник 2].

Структурирование и хранение данных

Все данные базы данных Firebase Realtime хранятся в виде объектов JSON. Вы можете думать о базе данных как о дереве JSON, размещенном в облаке. В отличие от базы данных SQL, здесь нет таблиц или записей. Когда вы добавляете данные в дерево JSON, оно становится узлом в существующей структуре JSON со связанным ключом.
Хотя база данных использует дерево JSON, данные, хранящиеся в базе данных, могут быть представлены в виде определенных собственных типов, которые соответствуют доступным типам JSON, что обеспечивает возможность написать более поддерживаемый код. Например, рассмотрим приложение чата, которое позволяет пользователям сохранять базовый профиль и список контактов. Профиль пользователя находится, например, в /users/$uid. У пользователя alovelace может быть запись в базе данных, которая выглядит примерно так:

{ "users" : { "alovelace" : { "name" : "Ada Lovelace" , "contacts" : { "ghopper" : true }, }, "ghopper" : { ... }, "eclarke" : { . .. } } }

База данных Firebase Realtime позволяет вкладывать данные глубиной до 32 уровней, однако не стоит думать, что так структура выглядит по умолчанию. Таким образом, когда вы выбираете данные в каком-либо месте в вашей базе данных, вы также получаете все его дочерние узлы. Кроме того, когда вы предоставляете кому-то доступ для чтения или записи в узле вашей базы данных, вы также предоставляете им доступ ко всем данным в этом узле. Поэтому на практике лучше сохранять структуру данных как можно более плоской.
В качестве примера того, почему вложенные данные являются плохими, рассмотрим следующую структуру с множественными вложениями:

{
  // это плохо вложенная архитектура данных
  // для узла "чаты" для получения списка заголовков разговоров требуется
  // возможно загрузка сотен мегабайт сообщений
  "чаты" : { 
    "один" : { 
      "title" : "Пионеры исторических технологий" , 
      "Сообщения" : { 
        "m1" : { "sender" : "ghopper" , "message" : "Обнаружена неисправность реле. Причина: моли."      },
        "m2" : {...}, 
        // очень длинный список сообщений
      }
    },
    "два" : { ... }   
  }
}

При таком вложенном дизайне итерация по данным становится проблематичной. Например, для перечисления заголовков разговоров в чате требуется, чтобы всё дерево чаты, включая всех участников и сообщения, было загружено на клиент. [Источник 3].

Ограничения базы данных

Общие ограничения

Общие ограничения
Операция Предел Описание
Одновременные соединения 200000 Одновременное подключение эквивалентно одному мобильному устройству, вкладке браузера или приложению сервера, подключенному к базе данных.

Это не то же самое, что общее количество пользователей вашего приложения, потому что ваши пользователи не все подключаются одновременно. Например, приложения с 10 миллионами активных пользователей в месяц обычно имеют менее 200 000 одновременных подключений. Максимальное количество одновременных подключений зависит от общего числа пользователей и среднего времени, которое пользователи проводят в вашем приложении.

Одновременные ответы от одной базы данных. ~ 100 000 Ответы включают одновременные операции широковещания и чтения, отправленные сервером из одной базы данных в данный момент времени. Ограничение относится к пакетам данных, которые представляют каждую отдельную операцию чтения или широковещания, включая push-уведомления, отправленные из базы данных.
Количество облачных функций, вызванных одной записью 1000 Хотя нет ограничений на количество операций чтения или записи, которые можно инициировать с помощью одной функции, одна операция записи в базу данных может вызвать только 1000 функций.

Облачные функции могут запускаться только операциями записи, и каждая функция также может запускать больше операций записи, которые запускают больше функций (каждая со своим собственным пределом в 1000 функций).

Размер одного события, вызванного записью 1 МБ Размер события состоит из следующих значений:
  1. Существующие данные в месте записи.
  2. Значение обновления или дельта в данных, необходимых для записи новых данных в местоположение.

Операции записи размером более 1 МБ успешно выполняются в базе данных, но они не инициируют вызов функции.

Передача данных в облачные функции 10 МБ/с Скорость данных о событиях, которые могут быть переданы в облачные функции.

Строгая проверка по умолчанию включена для операций записи, запускающих события. Любые операции записи, которые запускают более 1000 облачных функций или одно событие размером более 1 МБ, завершатся неудачно и вернут ошибку, сообщающую о достигнутом пределе. Это значит, что некоторые облачные функции вообще не запускаются, если они не проходят предварительную проверку. Если вы выполняете операцию записи большего размера (например, удаляете всю базу данных), вы можете отключить эту проверку, поскольку сами ошибки могут блокировать операцию. Чтобы отключить strictTriggerValidation, выполните следующие действия:
1. Получите секрет базы данных во вкладке Учетные записи служб в настройках проекта в консоли Firebase в соответствии с рисунком 1.

Рисунок 1 - Секрет базы данных

2. Запустите следующий запрос CURL из командной строки:

curl -X PUT -d "false" https://<namespace>.firebaseio.com/.settings/strictTriggerValidation/.json?auth\=<SECRET>

Ограничения на хранение данных

Ограничения на хранение данных
Свойство Предел Описание
Максимальная глубина дочерних узлов 32 Каждый путь в вашем дереве данных должен быть менее 32 уровней.
Длина ключа 768 байт Ключи UTF-8 кодируется и не могут содержать новые строки или любой из следующих символов: . $ # [] / или любые управляющие символы ASCII (0x00 - 0x1F и 0x7F)
Максимальный размер строки 10 МБ Данные в кодировке UTF-8.

Ограничения на чтение

Ограничения на чтение данных
Описание Предел Описание
Размер одного ответа, обслуживаемого базой данных 256 МБ Размер данных, загружаемых из базы данных в одном месте, должен быть менее 256 МБ для каждой операции чтения. Чтобы выполнить загрузку данных, превышающих 256 МБ, необходимо использовать разбиение данных.
Общее количество узлов в пути с прослушивателями или запросами на нем 75 миллионов Вы не можете прослушивать или запрашивать пути с более чем 75 миллионами узлов, совокупно. Однако вы все равно можете прослушивать или запрашивать дочерние узлы. Попробуйте углубиться в путь или создать отдельных слушателей или запросы для более конкретного пути.
Время выполнения одного запроса 15 мин. Один запрос может быть выполнен за 15 минут до сбоя.

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

Ограничения на запись

Ограничения на запись данных
Описание Предел Описание
Размер одного запроса на запись в базу данных 256 МБ от REST API; 16 МБ из SDK. Общий объем данных в каждой операции записи должен быть менее 256 МБ. Многопутевые обновления имеют одинаковое ограничение по размеру.
Запись байтов 64 МБ / мин Общее количество байтов, записанных с помощью одновременных операций записи в базу данных в любой момент времени.

[Источник 4].

Безопасность

Правила безопасности

Правила базы данных Firebase Realtime определяют, кто имеет права на чтение и запись в вашу базу данных, как структурированы ваши данные и какие индексы существуют. Эти правила располагаются на серверах Firebase и применяются автоматически в любое время. Каждый запрос на чтение и запись будет выполнен, только удовлетворяет правилам. По умолчанию правила не разрешают кому-либо доступ к вашей базе данных. Это необходимо для защиты базы данных от злоупотреблений до тех пор, пока у вас не будет времени настроить свои правила или настроить аутентификацию.
Правила базы данных реального времени имеют JavaScript-подобный синтаксис и бывают четырех типов:

Виды правил базы данных реального времени
Виды правил
.read Описывает, разрешено ли чтение данных пользователям.
.write Описывает возможность и время записи данных.
.validate Определяет, как будет выглядеть правильно отформатированное значение, имеет ли оно дочерние атрибуты, и тип данных.
.indexOn Определяет дочерний элемент для индексации для поддержки упорядочения и запросов.

База данных Firebase Realtime предоставляет полный набор инструментов для управления безопасностью вашего приложения. Эти инструменты облегчают аутентификацию ваших пользователей, обеспечение прав пользователей и проверку входных данных.

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

Аутентификация

Распространенным первым шагом в защите вашего приложения является идентификация ваших пользователей. Этот процесс называется аутентификацией . Вы можете использовать Firebase Authentication, чтобы пользователи могли войти в ваше приложение. Аутентификация в Firebase включает в себя поддержку обычных методов аутентификации, таких как Google и Facebook, а также вход по электронной почте и паролю, анонимный вход и многое другое.

Идентификация пользователя является важной концепцией безопасности. У разных пользователей разные данные, а иногда у них разные возможности. Например, в приложении чата каждое сообщение связано с пользователем, который его создал. Пользователи также могут удалять свои сообщения, но не сообщения других пользователей.

Авторизация

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

{ "rules" : { "foo" : { ".read" : true , ".write" : false } } }

Правила базы данных реального времени включают в себя встроенные переменные и функции, которые позволяют ссылаться на другие пути, временные метки на стороне сервера, информацию об аутентификации и многое другое. Вот пример правила, которое предоставляет доступ на запись для аутентифицированных пользователей /users/<uid>/, где <uid> - это идентификатор пользователя, полученный с помощью Firebase Authentication.

{ "rules" : { "users" : { "$ uid" : { ".write" : "$ uid === auth.uid" } } } }

Валидация данных

База данных Firebase Realtime является нереляционной. Это позволяет легко что-то менять по мере разработки, но как только ваше приложение будет готово выходу в массы, важно, чтобы данные оставались согласованными. Язык правил включает в себя .validate правило, которое позволяет применять логику проверки с использованием тех же выражений, что .read и для .write правил. Единственное отличие состоит в том, что правила проверки не каскадируются, поэтому все соответствующие правила проверки должны иметь значение true для разрешения записи.

Эти правила обеспечивают, что записываемые данные /foo/ должны быть строкой длиной менее 100 символов:

{ 
  "rules": { 
    "foo": { 
      ".validate": "newData.isString () && newData.val (). length <100" 
    } 
  } 
}

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

Определение индексов базы данных

База данных Firebase Realtime позволяет упорядочивать и запрашивать данные. Для небольших размеров данных база данных поддерживает специальные запросы, поэтому индексы, как правило, не требуются во время разработки. Однако перед запуском приложения важно указать индексы для любых запросов, которые необходимо выполнить, чтобы они продолжали работать по мере роста вашего приложения.

Индексы указываются с использованием .indexOn правила. Вот пример объявления индекса, который будет индексировать поля высоты и длины для списка динозавров:

{  "rules" : { "динозавры" : { ".indexOn" : [ "height" , "length" ] } } }

[Источник 5].


Работа с Android

Подключение к Firebase

  1. Откройте свой проект Android в Android Studio.
  2. Выберите Tools>Firebase, чтобы открыть панель Assistant.
  3. Выберите продукт Firebase для добавления в ваше приложение.
  4. Нажмите Connect to Firebase, чтобы подключить ваш проект Android к Firebase.
  5. На панели Firebase Assistant нажмите кнопку, чтобы добавить зависимость библиотеки для выбранного вами продукта Firebase.
  6. Синхронизируйте ваше приложение, чтобы убедиться, что все зависимости имеют необходимые версии.
  7. На панели Firebase Assistant следуйте оставшимся инструкциям по настройке для выбранного вами продукта Firebase.

Создание базы данных

Использование Android Studio Firebase Assistant (требуется дополнительная настройка)

  1. Создайте проект Firebase: в консоли Firebase нажмите « Добавить проект» , затем следуйте инструкциям на экране, чтобы создать проект Firebase или добавить службы Firebase в существующий проект.
  2. Перейдите в раздел « База данных » консоли Firebase . Вам будет предложено выбрать существующий проект Firebase. Следуйте процессу создания базы данных.
  3. Выберите режим запуска для ваших правил безопасности Firebase.
  4. Нажмите Готово
  5. Добавьте SDK базы данных в реальном времени в свое приложение.
  6. Настройте правила базы данных в реальном времени.

Создание базы данных через консоль(Альтернативный способ)

  1. Создание проекта Firebase
  2. Регистрация своего приложения в Firebase
  3. Добавление файла конфигурации Firebase
  4. Добавление Firebase SDK в ваше приложение

Все шаги разобраны на видео ниже

Чтение и запись

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

private DatabaseReference mDatabase;
// ...
mDatabase = FirebaseDatabase.getInstance().getReference();

Для основных операций записи вы можете использовать их setValue() для сохранения данных по указанной ссылке, заменяя любые существующие данные по этому пути. Вы можете использовать этот метод для:

  • Передачи типов, которые соответствуют доступным типам JSON, следующим образом:
    • String
    • Long
    • Double
    • Boolean
    • Map<String, Object>
    • List<Object>
  • Передачи пользовательского объект Java, если класс, который его определяет, имеет конструктор по умолчанию, который не принимает аргументов и имеет общедоступные методы получения назначаемых свойств.

Если вы используете объект Java, содержимое вашего объекта автоматически сопоставляется с дочерними расположениями во вложенном виде. Использование объекта Java также обычно делает ваш код более читабельным и простым в обслуживании. Например, если у вас есть приложение с базовым профилем пользователя, ваш User объект может выглядеть следующим образом:

@IgnoreExtraProperties
public class User {

    public String username;
    public String email;

    public User() {
        // Default constructor required for calls to DataSnapshot.getValue(User.class)
    }

    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }

}

Вы можете добавить пользователя setValue() следующим образом:

private void writeNewUser(String userId, String name, String email) {
    User user = new User(name, email);

    mDatabase.child("users").child(userId).setValue(user);
}

Использование setValue() таким способом перезаписывает данные в указанном месте, включая любые дочерние узлы. Однако вы все равно можете обновить дочерний элемент, не переписывая весь объект. Если вы хотите разрешить пользователям обновлять свои профили, вы можете обновить имя пользователя следующим образом:

mDatabase.child("users").child(userId).child("username").setValue(name);

Чтобы прочитать данные по пути и прослушать изменения, используйте метод addValueEventListener() или addListenerForSingleValueEvent() чтобы добавить ValueEventListener к DatabaseReference.
Вы можете использовать onDataChange() метод для чтения статического снимка содержимого по заданному пути, поскольку они существовали на момент события. Этот метод запускается один раз, когда слушатель подключен, и снова каждый раз, когда изменяются данные, включая дочерние. Обратному вызову события передается снимок, содержащий все данные в этом месте, включая дочерние данные. Если данных нет, снимок вернется false при вызове exists() и null при вызове getValue().
При этом важно понимать, что метод вызывается каждый раз , когда изменяются данные в указанной ссылке базы данных, в том числе изменения потомков. Чтобы ограничить размер снимков, прикрепляйте их только на самом высоком уровне, необходимом для просмотра изменений. Например, присоединение слушателя к корню вашей базы данных не рекомендуется.
В следующем примере демонстрируется приложение для ведения блогов в социальных сетях, которое извлекает сведения о записи из базы данных:

ValueEventListener postListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        // Get Post object and use the values to update the UI
        Post post = dataSnapshot.getValue(Post.class);
        // ...
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
        // ...
    }
};
mPostReference.addValueEventListener(postListener);

Слушатель получает объект DataSnapshot, который содержит данные в указанном месте в базе данных во время события. Вызов getValue() для снимка возвращает представление данных в Java-объекте. Если в этом месте нет данных, вызов getValue() возвращается null.

В этом примере ValueEventListener также определяется onCancelled() метод, который вызывается, если чтение отменено. Например, чтение может быть отменено, если у клиента нет разрешения на чтение из базы данных Firebase. Этому методу передается DatabaseError объект, указывающий причину сбоя.

В некоторых случаях вы можете захотеть вызвать обратный вызов один раз, а затем немедленно удалить его, например, при инициализации элемента пользовательского интерфейса, который вы не ожидаете изменить. Вы можете использовать addListenerForSingleValueEvent() метод, чтобы упростить этот сценарий: он запускается один раз, а затем не запускается снова.

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

Обновление или удаление данных

Для одновременной записи в определенные дочерние узлы без перезаписи других дочерних узлов используйте updateChildren() метод.

При вызове updateChildren() вы можете обновить дочерние значения нижнего уровня, указав путь к ключу. Если данные хранятся в нескольких местах для лучшего масштабирования, вы можете обновить все экземпляры этих данных, используя разветвление данных . Например, приложение для блогов в социальных сетях может иметь такой Post класс:

@IgnoreExtraProperties
public class Post {

    public String uid;
    public String author;
    public String title;
    public String body;
    public int starCount = 0;
    public Map<String, Boolean> stars = new HashMap<>();

    public Post() {
        // Default constructor required for calls to DataSnapshot.getValue(Post.class)
    }

    public Post(String uid, String author, String title, String body) {
        this.uid = uid;
        this.author = author;
        this.title = title;
        this.body = body;
    }

    @Exclude
    public Map<String, Object> toMap() {
        HashMap<String, Object> result = new HashMap<>();
        result.put("uid", uid);
        result.put("author", author);
        result.put("title", title);
        result.put("body", body);
        result.put("starCount", starCount);
        result.put("stars", stars);

        return result;
    }

}

Чтобы создать публикацию и одновременно обновить ее до ленты последних действий и ленты активности пользователя публикации, приложение для ведения блогов использует следующий код:

private void writeNewPost(String userId, String username, String title, String body) {
    // Create new post at /user-posts/$userid/$postid and at
    // /posts/$postid simultaneously
    String key = mDatabase.child("posts").push().getKey();
    Post post = new Post(userId, username, title, body);
    Map<String, Object> postValues = post.toMap();

    Map<String, Object> childUpdates = new HashMap<>();
    childUpdates.put("/posts/" + key, postValues);
    childUpdates.put("/user-posts/" + userId + "/" + key, postValues);

    mDatabase.updateChildren(childUpdates);
}

В этом примере показано, как создать сообщение в узле, содержащее сообщения для всех пользователей, и одновременно получить ключ с помощью getKey(). Затем ключ можно использовать для создания второй записи в сообщениях пользователя по адресу /user-posts/$userid/$postid.

Используя эти пути, вы можете выполнять одновременное обновление нескольких местоположений в дереве JSON с помощью одного вызова updateChildren(), например, как в этом примере создается новое сообщение в обоих местоположениях. Одновременные обновления, сделанные таким способом, являются атомарными: либо все обновления завершаются успешно, либо все обновления завершаются неудачно.

Самый простой способ удалить данные - вызвать removeValue() для данных.

Вы также можете удалить данные, указав null в качестве значения другую операцию записи, например setValue() или updateChildren(). Вы можете использовать эту технику updateChildren() для удаления нескольких дочерних элементов за один вызов API.[Источник 6].

Источники

  1. Firebase RealTime Database // Firebase Guides. URL: https://firebase.google.com/docs/database?hl=ru (дата обращения: 11.05.2020)
  2. Introduction // Firebase Guides. URL: https://firebase.google.com/docs/database (дата обращения: 11.05.2020)
  3. Structure data // Firebase Guides. URL: https://firebase.google.com/docs/database/android/structure-data (дата обращения: 11.05.2020)
  4. Realtime Database Limits // Firebase Guides. URL: https://firebase.google.com/docs/database/usage/limits
  5. Understand Firebase Realtime Database Rules // Firebase Guides. URL: https://firebase.google.com/docs/database/security (дата обращения: 11.05.2020)
  6. Installation & Setup on Android // Firebase Guides. URL: https://firebase.google.com/docs/database/android/start (дата обращения: 11.05.2020)