Избранное | Русский | Войти

Обзор API Java для хранилища данных

Хранилище данных Google App Engine представляет собой надежное масштабируемое место для хранения данных вашего веб-приложения. При разработке хранилища данных учитывались особенности работы веб-приложений. Особое внимание было уделено эффективности считывания данных и выполнения запросов. Данные хранятся в виде объектов, обладающих свойствами и упорядоченных по типам, которые определяются в приложении. Хранилище может выполнять запросы для однотипных объектов, применяя фильтры и различные порядки сортировки к ключам и значениям свойств. Для быстрого получения результатов из очень больших наборов данных все запросы предварительно индексируются. Хранилище данных поддерживает транзакционные обновления, используя группы объектов, определенные в приложении, как блоки для проведения транзакций в распределенной сети данных.

Хранилище данных: введение

В хранилище данных App Engine данные хранятся в виде так называемых объектов, для которых выполняются запросы. Объект обладает одним или несколькими свойствами – именованными значениями одного из поддерживаемых типов данных. Свойство может являться ссылкой на другой объект.

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

В отличие от обычных баз данных, в хранилище данных используется распределенная архитектура, которая позволяет управлять масштабированием больших наборов данных. Приложение App Engine может оптимизировать способ распределения данных, описывая отношения между объектами данных или определяя индексы для запросов.

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

Интерфейсы данных Java

Для объектов хранилища данных не существует схемы: два объекта одного типа не должны обладать одним набором свойств и использовать одни типы значений для одинаковых свойств. Если необходимо соответствие схеме, это нужно предусмотреть в самом приложении. Для моделирования и хранения данных SDK Java содержит реализации интерфейсов объектов данных Java (JDO) и Java Persistence API (JPA). Эти стандартные механизмы включают интерфейсы для определения классов и объектов данных и выполнения запросов. Кроме того, хранилище данных предоставляет низкоуровневый API, который можно использовать напрямую в приложении или для реализации других адаптеров интерфейсов.

В этом документации подробно описаны JDO. Информация о JDO из других источников также применима к App Engine. Используйте это руководство, чтобы узнать о различиях в реализации и нереализованных функциях.

Помимо JDO поддерживается и JPA. Этот API кратко описывается в статье Использование JPA.

При создании классов JDO используются аннотации классов Java ("старых добрых простых Java-объектов" или "POJO"), чтобы описать хранение экземпляров классов в хранилище данных и их воссоздание при получении из хранилища данных. Вот пример простого класса данных:

Employee.java

import java.util.Date;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Employee {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;

    @Persistent
    private String firstName;

    @Persistent
    private String lastName;

    @Persistent
    private Date hireDate;

    public Person(String firstName, String lastName, Date hireDate) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.hireDate = hireDate;
    }

    // Accessors for the fields.  JDO doesn't use these, but your application does.

    public Long getId() {
        return id;
    }

    public String getFirstName() {
        return firstName;
    } 

    // ... other accessors...
}

С хранилищем данных можно взаимодействовать, используя объект PersistenceManager, который можно получить из объекта PersistenceManagerFactory. Поскольку создание объекта PersistenceManagerFactory требует много ресурсов, нужно удостовериться, что экземпляр этого класса для приложения создается только один раз (он может обрабатывать множество запросов). Это нетрудно сделать, создав отдельный класс-оболочку подобным образом:

PMF.java

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;

public final class PMF {
    private static final PersistenceManagerFactory pmfInstance =
        JDOHelper.getPersistenceManagerFactory("transactions-optional");

    private PMF() {}

    public static PersistenceManagerFactory get() {
        return pmfInstance;
    }
}

Можно создать экземпляры классов данных и сохранить их в хранилище данных:

import java.util.Date;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;

import Employee;
import PMF;

// ...
        Employee employee = new Employee("Alfred", "Smith", new Date());

        PersistenceManager pm = PMF.get().getPersistenceManager();

        try {
            pm.makePersistent(employee);
        } finally {
            pm.close();
        }

JDO включает интерфейс запросов JDOQL. Его можно использовать, чтобы получить объекты в виде экземпляров этого классов следующим образом:

import java.util.List;
import Employee;

// ...
        String query = "select from " + Employee.class.getName() + " where lastName == 'Smith'";
        List<Employee> employees = (List<Employee>) pm.newQuery(query).execute();

Объекты и свойства

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

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

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

В отличие от реляционных баз данных, хранилище данных App Engine не требует, чтобы все объекты одного типа обладали одинаковыми свойствами. Приложение может указать и принудительно использовать свою модель данных с помощью библиотек, содержащихся в SDK, или собственного кода.

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

Запросы и индексы

Запрос хранилища данных App Engine выполняется для каждого объекта заданного типа (класса данных). В запросе можно указать ноль или более фильтров для ключей и значений свойств объектов, а также ноль или более порядков сортировки. Объект возвращается в качестве результата запроса в том случае, если у него есть хотя бы одно значение (даже нулевое) для каждого свойства, указанного в фильтрах и порядках сортировки запроса, и значения свойств соответствуют всем условиям фильтров.

Каждый запрос к хранилищу данных использует индекс – таблицу, которая содержит результаты запроса, расположенные в необходимом порядке. Приложение App Engine определяет свои индексы в файле конфигурации. Веб-сервер разработки автоматически добавляет в этот файл записи, когда сталкивается с запросами, индексы для которых еще не настроены. Индексы можно настраивать вручную, редактируя файл перед загрузкой приложения. Как только приложение изменяет объекты хранилища данных, хранилище обновляет индексы, корректируя результаты. Когда приложение выполняет запрос, хранилище данных получает результаты напрямую из соответствующего индекса.

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

Транзакции и группы объектов

В хранилище данных App Engine каждая попытка создать, обновить или удалить объект выполняется в транзакции. Транзакция обеспечивает сохранение всех изменений объекта в хранилище данных, а в случае сбоя – отсутствие каких-либо изменений. Это гарантирует согласованность данных в объекте.

С помощью API транзакций можно выполнять по несколько действий над объектом в одной транзакции. Допустим, вам нужно увеличить значение счетчика в объекте. Для этого нужно считать значение счетчика, вычислить новое значение и сохранить его. Без транзакции другой процесс может увеличить значение счетчика в перерыве между тем, как вы считали его, и тем, как вы обновите его. Это приведет к тому, что ваше приложение перезапишет уже обновленное значение. Выполнение считывания, вычисления и записи в одной транзакции позволяет гарантировать, что никакой другой процесс не повлияет на выполнение вашей операции.

В одной транзакции можно вносить изменения и в несколько объектов. Для обеспечения такой возможности App Engine необходимо заранее знать, какие объекты будут обновляться совместно, чтобы хранить их способом, поддерживающим транзакции. При создании объекта вы должны объявить, что он принадлежит к той же группе объектов, что и другой объект. Все объекты, получаемые, создаваемые, обновляемые или удаляемые в одной транзакции, должны принадлежать к одной и той же группе объектов.

Группы объектов определяются иерархией отношений между объектами. Чтобы создать объект в группе, следует объявить, что он является дочерним по отношению к другому объекту, уже состоящему в этой группе. Этот другой объект называется родительским. Объект, создаваемый без родителя, называется корневым объектом. Корневой объект без потомков является единственным объектом в группе. У каждого объекта есть путь, состоящий из родительско-дочерних отношений, который ведет от корневого объекта к данному (самый короткий путь бывает при отсутствии родителя). Этот путь является важной частью полного ключа объекта. Полный ключ может быть представлен типом и идентификатором либо именем ключа каждого объекта, входящего в путь.

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

Квоты и ограничения

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

Данные, которые приложение отправляет в хранилище данных, учитываются относительно квоты данных, отправленных в API (хранилища данных). Данные, получаемые приложением из хранилища данных, учитываются относительно квоты данных, полученных из API (хранилища данных).

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

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

  • Процессорное время (регулируется)
  • Процессорное время хранилища данных

Подробнее о квотах рассказано в разделе Квоты и в разделе Консоли администрирования "Сведения о квотах".

Помимо квот, к использованию хранилища данных применяются следующие ограничения:

Ограничение Величина
Максимальный размер объекта 1 мегабайт
Максимальное количество значений в индексе для объекта (1) 1000 значений
Максимальное количество объектов при пакетном сохранении или пакетном удалении 500 объектов
Максимальное количество объектов при пакетном получении 1000 объектов
Максимальный сдвиг результатов в запросе 1000
  1. Во всех индексах объект использует только одно значение для указания каждого столбца и строки, которые к нему относятся. Количество значений для объекта в индексе может вырасти, если у индексированного свойства несколько значений, для описания которых в таблице требуется несколько строк с повторяющимися значениями.