Содержание


Java development 2.0

Часть 1. Вторая волна разработки Java-приложений: «Облачное» хранилище средствами Amazon SimpleDB

Познакомьтесь с сервисом SimpleDB и возможностями Amazon SDK

Comments

Серия контента:

Этот контент является частью # из серии # статей: Java development 2.0

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Java development 2.0

Следите за выходом новых статей этой серии.

Развить навыки по этой теме

Этот материал — часть knowledge path для развития ваших навыков. Смотри Использование NoSQL для анализа данных большого объема

Статьи данной серии посвящены рассмотрению ряда нереляционных хранилищ данных, объединенных общим названием NoSQL. В последней статье мы исследовали принципиальные отличия документо-ориентированного хранилища (CouchDB) от схемо-ориентированных реляционных баз данных. Помимо отличий в структуре хранения данных, CouchDB использует REST API и поддерживает новые средства выполнения запросов, а именно функции MapReduce, заданные в JavaScript. Очевидно, что подобные технологии являются значительным отрывом от традиционного мира JDBC.

Одна из моих последних статей была посвящена Google Bigtable, которая не является реляционной или документно-ориентированной базой данных (и, в частности, ни в какой мере не поддерживает JDBC). Bigtable относится к хранилищам типа «ключ-значение». Это означает, что данное хранилище не использует жесткую схему данных, и вы можете хранить там практически любую нужную вам информацию: парковочные квитанции, программу соревнований или данные об участниках забегов. Отказ от использования жесткой схемы данных обеспечивает Bigtable уникальную гибкость и открывает новые возможности для быстрой разработки приложений.

Bigtable - не единственное хранилище типа «ключ-значение», доступное разработчикам приложений. Amazon представляет свое собственное «облачное» хранилище данных - Amazon SimpleDB. В то время как доступ Java-разработчиков к Bigtable осуществляется через уровень абстракции, реализуемый Google App Engine, доступ к Amazon SimpleDB организован как веб-сервис. Таким образом, вы можете управлять базой данных, основанной на Amazon SimpleDB, с помощью веб-технологий и HTTP. Компоновка на базе инфраструктуры Amazon Web Service позволяет вам использовать все возможности SimpleDB с помощью наиболее предпочтительного для вас языка программирования, – в список поддерживаемых языков входят PHP, Ruby, C# и Java.

В статье этого месяца мы рассмотрим возможности работы с SimpleDB средствами официального SDK компании Amazon. Для демонстрации возможностей лексикографического поиска, одной из наиболее привлекательных черт этого мощного «облачного» хранилища, мы вновь воспользуемся уже знакомым примером с соревнованиями.

Знакомьтесь - SimpleDB

По внутреннему устройству SimpleDB – это масштабируемое хранилище данных высокой доступности, написанное на Erlang. Концепция этого сервиса аналогична сервису Amazon S3. В то время как S3 оперирует объектами, заключенными в контейнеры, базу данных в терминах SimpleDB можно логически определить как набор доменов, содержащие различные элементы. SimpleDB также поддерживает различные атрибуты элементов. Понятие «домен» можно рассматривать как аналог контейнера в S3 или таблицы в реляционной базе данных (или, что более правильно, как аналог понятия «сущности» в Bigtable). Однако я хочу предостеречь вас от попытки спроецировать концепцию хранения информации в SimpleDB в термины и представления реляционных баз данных – это хранилище так же далеко от баз с жестко определенной схемой данных, как и Bigtable. Домены могут содержать множество элементов (которые можно рассматривать как аналоги записей в таблице), а элементы могут иметь множество атрибутов (аналог столбцов в реляционном мире).

Атрибуты представляют собой пары «имя/значение» (напоминает Bigtable, не правда ли?), при этом понятие «пара» не ограничивается единственным соответствием. Это значит, что имени атрибута может соответствовать коллекция (или список) значений. Элемент «слово» может, например, иметь многозначные значения атрибута. Что более важно, все данные, хранящиеся в SimpleDB, определены как String, что в корне отличается от представления данных в Bigtable или в стандартных СУБД, которые, как правило, поддерживают несметное количество типов данных.

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

Элементы не обладают ключами «в чистом виде» (в отличие от реализации ключей в Bigtable). Ключом или уникальным идентификатором элемента является его имя. SimpleDB обладает достаточным интеллектом, чтобы при получении запроса на создание элемента с уже использующимся именем вместо создания нового элемента изменить уже существующий (в том случае, если значения атрибутов нового элемента отличны от исходных значений).

Аналогично другим сервисам семейства Amazon Web Services, SimpleDB позволяет обращаться к содержимому средствами HTTP. В результате в вашем распоряжении оказывается огромное количество способов взаимодействия с базой данных. В мире Java спектр средств взаимодействия варьируется от собственного SDK компании Amazon (которым мы воспользуемся в приведенных далее примерах) до популярного проекта Topica или полномасштабной реализации JPA (которую мы подробно рассмотрим в части 2).

Марафон в облаках

В рамках данной серии статей для демонстрации различных возможностей технологии Java 2.0 я использовал в качестве примеров соревнования по бегу и парковочные квитанции. Рассматривая уже знакомые примеры, нам будет проще понять общие черты и оценить различия между системами. Сохраним же нашу приверженность финишной линии и рассмотрим, как участники забегов могут быть представлены в Amazon SimpleDB.

В SimpleDB соревнования могут быть представлены в виде домена. Каждый экземпляр соревнований будет являться отдельным элементом базы SimpleDB, а название и дата проведения соревнований будут атрибутами этого элемента (каждый атрибут будет иметь свое значение). Здесь важно помнить, что название соревнований в нашем случае – это тоже атрибут, а не имя элемента. Название, которое вы присваиваете элементу базы данных, становится его ключевым индексом. Так, например, индексом элемента может быть название марафонского забега. В качестве альтернативного решения мы можем определить индекс как дату проведения забега, – это позволит нам присваивать уникальные имена периодическим соревнованиям (многие из них проводятся ежегодно). Такой подход позволит нам хранить в базе SimpleDB данные о состязаниях, которые проводятся, к примеру, два раза в год.

Аналогично, участники соревнований также определяются как домен. Отдельные участники будут являться элементами базы данных, при этом имя и возраст участника будут атрибутами соответствующего элемента. Аналогично элементам-соревнованиям, элементы-участники должны иметь уникальные имена (чтобы, например, не перепутать Пита Смита с Марти Ховардом). В отличие от Bigtable, SimpleDB все равно, как вы назовете элементы базы данных; это хранилище не предоставляет своим пользователям генератор значений ключей. В нашем случае мы можем в качестве имени для каждого участника гонки использовать временную метку или просто увеличивать счетчик, например: runner_1, runner_2 и т.д.

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

Начинаем работу с Amazon SDK

Недавно компания Amazon стандартизировала библиотеку, содержащую функции для работы со всеми веб-сервисами компании, в том числе и с SimpleDB. Как и большинство других библиотек, эта библиотека обеспечивает уровень абстракции для доступа и взаимодействия с сервисами, который позволяет клиентскому приложению работать в «родной» среде. Так, например, Java-библиотека Amazon для SimpleDB позволяет вам создавать домены и элементы, обращаться к ним с запросами, изменять и удалять их из хранилища, совершенно не беспокоясь при этом о том, как соответствующие операции передаются по HTTP в облако.

В листинге 1 приведен пример Java-кода, который создает клиент AmazonSimpleDBClient и добавляет новый домен Races (чтобы воспроизвести этот пример на своем рабочем компьютере, вам потребуется регистрация на Amazon)

Листинг 1. Создание экземпляра AmazonSimpleDBClient
AmazonSimpleDB sdb = new AmazonSimpleDBClient(new PropertiesCredentials(
                new File("etc/AwsCredentials.properties")));
String domain = "Races";
sdb.createDomain(new CreateDomainRequest(domain));

Обращаю ваше внимание на то, что шаблон Amazon SDK Request будет присутствовать во всех операциях с SimpleDB. В данном случае вызов CreateDomainRequest порождает домен. С помощью клиентского метода batchPutAttributes я могу создать элементы домена. Эта операция будет использовать список List элементов, как показано в листинге 2.

Листинг 2. Race_01
List<ReplaceableItem> data = new ArrayList<ReplaceableItem>();

data.add(new ReplaceableItem().withName("Race_01").withAttributes(
   new ReplaceableAttribute().withName("Name").withValue("Charlottesville Marathon"),
   new ReplaceableAttribute().withName("Distance").withValue("26.2")));

В Amazon SDK элементы имеют тип ReplaceableItem. Вы определяете имя каждого элемента (задавая тем самым ключевой индекс), а потом можете добавить атрибуты этого элемента (как значения типа ReplaceableAttribute). В листинге 2 я создал соревнование с простым индексом Race_01. В листинге 3 я с помощью метода BatchPutAttributesRequest клиента AmazonSimpleDBClient добавлю этот элемент в домен Races:

Листинг 3. Создание элемента в SimpleDB
sdb.batchPutAttributes(new BatchPutAttributesRequest(domain, data));

Запросы в SimpleDB

После того как я сохранил элемент, соответствующий одному соревнованию, я могу выполнить поиск этого элемента с помощью языка запросов SimpleDB. Язык запросов SimpleDB очень похож на SQL, за исключением одной особенности. Помните, я упоминал, что все значения атрибутов хранятся в базе данных как строки? Это означает, что все данные сравниваются лексикографически, а когда речь идет о поиске данных, подобные сравнения могут вызвать определенные трудности.

Если я создам запрос, основанный на сравнении числовых значений, то SimpleDB будет проводить поиск по символам в записи числа, а не по его числовому значению. На данный момент у меня хранится только один элемент-соревнование, так что я легко найду его с помощью запроса SimpleDB, приведенного в листинге 4.

Листинг 4. Поиск элемента Race_01
String qry = "select * from `" + domain + "` where Name = 'Charlottesville Marathon'";
SelectRequest selectRequest = new SelectRequest(qry);
for (Item item : sdb.select(selectRequest).getItems()) {
 System.out.println("Race Name: " + item.getName());
}

Запрос в листинге 4 выглядит абсолютно как стандартный SQL-запрос. В рассматриваемом примере я просто прошу найти все элементы домена Race с именем "Charlottesville Marathon". Запрос SelectRequest клиенту AmazonSimpleDBClient возвращает коллекцию элементов. Следовательно, я смогу просмотреть все элементы и вывести их имена. В нашем случае в результате выполнения запроса мы получим единственный элемент домена.

Рассмотрим теперь, как изменится ситуация, если я добавлю еще одно состязание, но уже с другой дистанцией, так, как показано в листинге 5.

Листинг 5. Добавление нового элемента-соревнования с меньшей дистанцией
List<ReplaceableItem> data2 = new ArrayList<ReplaceableItem>();

data2.add(new ReplaceableItem().withName("Race_02").withAttributes(
   new ReplaceableAttribute().withName("Name").withValue("Charlottesville 1/2 Marathon"),
   new ReplaceableAttribute().withName("Distance").withValue("13.1")));

sdb.batchPutAttributes(new BatchPutAttributesRequest(domain, data2));

Поскольку у меня теперь два забега на разные дистанции, имеет смысл использовать длину дистанции как основной критерий поиска (см. листинг 6).

Листинг 6. Поиск по дистанции
String disQry = "select * from `" + domain + "` where Distance > '13.1'";
SelectRequest selectRequest = new SelectRequest(disQry);
for (Item item : sdb.select(selectRequest).getItems()) {
 System.out.println("Race Name: " + item.getName());
}

Естественно, в результате такого поиска будет найден единственный элемент Race_01. Это правильный результат, так как 26,2 больше, чем 13,1 и в математическом, и в лексикографическом смысле. Но посмотрите, что произойдет, если я добавлю в базу данных забег на действительно длинную дистанцию:

Листинг 7. Лисбергский ультрамарафон
List<ReplaceableItem> data3 = new ArrayList<ReplaceableItem>();

data3.add(new ReplaceableItem().withName("Race_03").withAttributes(
   new ReplaceableAttribute().withName("Name").withValue("Leesburg Ultra Marathon"),
   new ReplaceableAttribute().withName("Distance").withValue("103.1")));

sdb.batchPutAttributes(new BatchPutAttributesRequest(domain, data3));

В листинге 7 я добавил забег на дистанцию 103,1 мили. После обновления я вновь выполнил запрос из листинга 6, и угадайте, какой результат я получил? Совершенно верно: 103,1. Лексикографически это значение меньше, чем 13,1. Именно поэтому в результатах выполнения вашего запроса (если, конечно, вы следуете моим шагам, читая эту статью у себя дома) вы не увидите Лисбергского ультрамарафона!

Посмотрим, что получится в результате выполнения другого запроса, на этот раз для поиска забега на короткую дистанцию (см. листинг 8):

Листинг 8. Посмотрим, что мы найдем в результате такого запроса:
String disQry = "select * from `" + domain + "` where Distance < '13.1'";
SelectRequest selectRequest = new SelectRequest(disQry);
for (Item item : sdb.select(selectRequest).getItems()) {
 System.out.println("Race Name: " + item.getName());
}

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

Лексикографический поиск

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

Самая длинная дистанция рассматриваемых забегов составляет 103,6 мили (лично я никогда даже и не пытался пробежать такое расстояние!), что с лексикографической точки зрения представляет собой три знака слева от разделителя целой и дробной части. Так что все, что мне нужно сделать – добавить к остальным дистанциям ведущие нули так, чтобы все длины имели одинаковое число знаков. После подобного преобразования мой запрос на поиск по расстоянию будет возвращать правильный результат.

На рисунке 1 показан экран SDB Tool, плагина к браузеру Firefox, который позволяет выполнять запросы и обновлять домены Simple DB (см. Ресурсы):

Рисунок 1. Добавление ведущих нулей к длине дистанции
A screenshot showing races and distance values as displayed by SDB Tool
A screenshot showing races and distance values as displayed by SDB Tool

Как вы видите, я добавил по одному нулю к длинам дистанции для соревнований Race_01 и Race_02. С точки зрения непосвященного подобная операция не имеет особого смысла, однако это сильно упрощает процесс поиска в базе данных. Теперь, выполнив запрос на поиск соревнований с длиной дистанции меньше 020.0 мили (или просто 20 миль), я наконец-то получил правильные результаты (см. рисунок 2):

Рисунок 2. Выравнивание значений позволяет решить проблемы поиска
A screeenshot showing successful query results in SDB Tool
A screeenshot showing successful query results in SDB Tool

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

Взаимосвязи в SimpleDB

Связывание данных в SimpleDB не вызывает особенных проблем. Концептуально, для каждого экземпляра runner вы можете определить атрибут race и присвоить этому атрибуту в качестве значения название соответствующих соревнований (например, Race_01). Более того, ничто не может помешать вам хранить в качестве значения атрибута race несколько таких названий. Можно использовать прямо противоположный подход и хранить набор имен участников в качестве элементов домена Runners (как показано в листинге 9). Главное, о чем необходимо помнить, это то, что язык запросов Amazon не позволяет объединить два домена, все операции объединения должны выполняться средствами вашего приложения.

Листинг 9. Создание домена Runners с двумя элементами
sdb.createDomain(new CreateDomainRequest("Runners"));

List<ReplaceableItem> runners = new ArrayList<ReplaceableItem>();

runners.add(new ReplaceableItem().withName("Runner_01").withAttributes(
   new ReplaceableAttribute().withName("Name").withValue("Sally Smith")));

runners.add(new ReplaceableItem().withName("Runner_02").withAttributes(
	new ReplaceableAttribute().withName("Name").withValue("Richard Bean")));

sdb.batchPutAttributes(new BatchPutAttributesRequest("Runners", runners));

После того, как я создал домен Runners и внес в него пару элементов, я могу обновить существующий элемент соревнований и добавить к нему бегунов (см. листинг 10).

Листинг 10. Добавление двух участников соревнований
races.add(new ReplaceableItem().withName("Race_01").withAttributes(
  new ReplaceableAttribute().withName("Name").withValue("Charlottesville Marathon"),
  new ReplaceableAttribute().withName("Distance").withValue("026.2"),
  new ReplaceableAttribute().withName("Runners").withValue("Runner_01"),
  new ReplaceableAttribute().withName("Runners").withValue("Runner_02")));

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

Удаление ненужных данных

Очень важно своевременно удалять данные, которые вам больше не понадобятся, так что я кратко остановлюсь на операции удаления данных с помощью Amazon SDK. Операция удаления мало чем отличается от операции создания данных или выполнения запросов: вы просто создаете запрос на удаление.

Листинг 11 демонстрирует операцию удаления элемента Race_01.

Листинг 11. Выполнение операции delete в SimpleDB
sdb.deleteAttributes(new DeleteAttributesRequest(domain, "Race_01"));

Если для удаления элемента используется запрос DeleteAttributesRequest, то какой запрос понадобится для удаления домена? Вы совершенно правы - DeleteDomainRequest!

Листинг 12 Удаление домена в SimpleDB
sdb.deleteDomain(new DeleteDomainRequest(domain));

Наш обзор еще не закончен!

На этом мы заканчиваем наш обзор возможностей Amazon SDK, однако наше изучение возможностей «облачных» сервисов на примере Amazon SimpleDB еще не закончено. Amazon SDK предоставляет целый ряд полезных функций, которые вы можете использовать в своих приложениях, однако если вам требуются рабочие модели – такие как модели соревнований и их участников - вы скорее всего предпочтете использовать более удобные инструменты, например, JPA. В следующем месяце в части 2 этой серии мы узнаем, что получится из объединения JPA с SimpleDB. А пока наслаждайтесь возможностями лексикографического поиска!


Ресурсы для скачивания


Похожие темы

  • Оригинал статьи (EN).
  • В серии статей Вторая волна разработки Java-приложений рассматриваются технологии и инструменты, меняющие принципы создания приложений на Java, в том числе CouchDB (ноябрь 2009 г.) и Bigtable (май 2010 г.).
  • Прочитав статью Облачные вычисления на платформе Amazon Web Services. Часть 5: обработка данных в облаке при помощи SimpleDB (Прабхакар Чаганти, Prabhakar Chaganti, developerWorks, февраль 2009 г.), вы узнаете о технологии SimpleDB от Amazon, а также о некоторых возможностях boto – открытой библиотеки для взаимодействия с CouchDB, написанной на Python.
  • В статье Итоговая согласованность - новый взгляд (Вернер Фогельс, Werner Vogels, All Things Distributed, декабрь 2008 г.) технический директор Amazon поможет вам разобраться с теоремой CAP Эрика Бьюера и ее влиянием на инфраструктуру веб-сервисов Amazon.
  • Прочитайте статью Шаблоны NoSQL (Рикки Хо, Ricky Ho, Pragmatic Programming Techniques, ноябрь 2009 г.), в которой приводится обзор и перечень СУБД NoSQL-типа, а также подробное рассмотрение их общих архитектурных принципов.
  • Ознакомьтесь со статьей Bigtable - распределенное хранилище структурированных данных (Фэй Чан и другие, Fay Chang et al., Google, ноябрь 2006 г.), в которой рассматривается Bigtable – база данных, которая проектировалась с учетом необходимости высокой масштабируемости (до петабайт данных, распределенных по разным серверам).
  • Загрузите Amazon SDK и используйте инфраструктуру Amazon Web Services для своих проектов (если вы еще не являетесь зарегистрированным пользователем Amazon, вам потребуется пройти регистрацию).
  • Установите SDB Tool – этот плагин Firefox существенно упростит вашу работу с Amazon SimpleDB.

Комментарии

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java
ArticleID=775338
ArticleTitle=Java development 2.0: Часть 1. Вторая волна разработки Java-приложений: «Облачное» хранилище средствами Amazon SimpleDB
publish-date=11162011