Вторая волна разработки Java-приложений: Базы данных типа NoSQL

Исходники

Реляционные СУБД занимают лидирующие позиции уже более 30 лет, однако эта ситуация может измениться в связи с растущей популярностью баз данных, не имеющих реляционных схем данных (или баз данных типа NoSQL). РСУБД предоставляют надежные средства хранения данных в системах с традиционной клиент-серверной архитектурой, которые, к сожалению, не всегда легко и дешево масштабируются путем добавления новых вычислительных узлов. Это становится весьма серьезным ограничением в эпоху таких Web-приложений, как Facebook и Twitter, которым хорошая масштабируемость крайне необходима.

Проблему масштабируемости не удалось решить ранним альтернативам реляционных СУБД (помните объектно-ориентированные базы данных?). В отличие от них СУБД NoSQL, подобные Bigtable и SimpleDB (от Google и Amazon соответственно), проектировались именно в расчете на высокую масштабируемость Web-приложений. NoSQL вполне могут оказаться решением критически важной проблемы масштабируемости, которая будет становиться все более актуальной по мере развития Web 2.0.

В этой статье серии Вторая волна разработки Java-приложений приводится введение в проектирование баз данных без использования схем, что представляет собой одну из главных трудностей при переходе на NoSQL для разработчиков, ранее работавших с реляционными СУБД. Вы увидите, что самое главное в этом процессе – начать проектирование с создания модели предметной области, а не реляционной модели. При работе с Bigtable (как в примерах ниже) вы можете рассчитывать на помощь Gaelyk – легковесной инфраструктуры, расширяющей возможности платформы Google App Engine.

NoSQL: Нужен ли новый взгляд на мир?

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

Проблемы, связанные с потребностями высокомасштабируемых Web-приложений, привели к появлению новых решений. В частности, Facebook не может полагаться на реляционную БД для хранения информации. Вместо этого в ней используется хранилище пар типа «ключ–значение», т.е. фактически – высокопроизводительная хэш-таблица. Созданное на ее основе решение (проект Cassandra) в настоящее время также используется сервисами Twitter и Digg, а недавно было передано организации Apache Software Foundation. Другим примером компании, чей рост требовал альтернативных подходов к хранению данных, является Google, в котором была создана технология Bigtable.

В нереляционных базах данных не используется соединение таблиц, первичные или вторичные ключи (ключи присутствуют, но в менее строгом виде). Таким образом, вы будете сильно разочарованы, попробовав применить реляционное моделирование при создании модели данных для базы данных NoSQL. Гораздо проще начать с описания предметной области. При этом лично мне доставляет удовольствие гибкость нереляционной структуры БД, лежащей в основе модели предметной области.

Таким образом, сложность перехода на нереляционную модель данных зависит от вашего подхода к проектированию приложений, а именно: начинаете вы с реляционной схемы или описания предметной области. Начав использовать СУБД, подобную CouchDB или Bigtable, вам придется распрощаться с удобными инфраструктурами хранения данных, например Hibernate. C другой стороны, перед вами откроются широкие возможности для самостоятельного создания такой технологии для своих нужд. А в процессе ее создания вы познакомитесь с тонкостями баз данных NoSQL.

В начало

Сущности и связи

Нереляционные СУБД позволяют проектировать модель предметной области в виде набора объектов, причем эта возможность упрощается такими современными решениями, как Gaelyk. На следующем этапе вам предстоит отобразить созданную модель на структуру базы данных, что в случае использования Google App Engine не представляет никаких трудностей.

Ранее, в статье Инфраструктура Gaelyk в приложениях для Google App Engine, вы познакомились с Gaelyk – Groovy-библиотекой, которая упрощает работу с хранилищем данных Google. В настоящей статье немало внимания будет уделяться объекту Entity в Google. В листинге 1 приведен пример из предыдущей статьи, демонстрирующий работу с сущностями (entity) в Gaelyk.

Листинг 1. Сохранение объекта в базе данных при помощи Entity
def ticket = new Entity(«ticket»)
ticket.officer = params.officer
ticket.license = params.plate
ticket.issuseDate = offensedate
ticket.location = params.location
ticket.notes = params.notes
ticket.offense = params.offense

Объектное проектирование

Предпочтение объектной, а не реляционной модели при проектировании базы данных прослеживается в современных инфраструктурах приложений, таких как Grails и Ruby on Rails. Эти платформы уделяют повышенное внимание именно созданию модели, беря на себя генерацию физической схемы базы данных.

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

В начало

Соревнования

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

Рисунок 1. Соревнования и их участники

При моделировании этих отношений в реляционной базе данных нам потребовались бы три таблицы – третья должна была бы служить для связывания соревнований и участников по принципу «многое-ко-многим». К счастью, нам этого делать не придется. Вместо этого мы будем использовать Gaelyk в Groovy для отображения этой модели на структуру Bigtable, предоставляемую платформой Google App Engine. Наша задача существенно упрощается благодаря тому, что Gaelyk позволяет работать с сущностями как с ассоциативными таблицами (Map).
Масштабирование и шарды

Шардинг – это вариант декомпозиции базы данных, при котором происходит репликация таблиц и логические распределение информации по вычислительным узлам. Например, на одном узле могут храниться данные обо всех учетных записях пользователей в США, а на другом – жителей Европы. Сложности шардинга обусловлены наличием связей между данными, размещенными на разных узлах. Это серьезная и сложная проблема, которая в ряде случаев не решается (в разделе Ресурсы приведена ссылка на мою дискуссию с Максом Россом из Google (Max Ross) на тему шардинга и сложностей масштабируемости реляционных баз данных).

Одной из привлекательных черт нереляционных моделей данных является то, что вам не требуется продумывать все детали заранее – последующие изменения могут вноситься гораздо проще, чем в реляционную схему. Учтите, я не имею в виду, что реляционную схему изменить нельзя. Это, безусловно, возможно, но задача упрощается при отсутствии схемы. В данный момент времени мы не будем описывать свойства объектов предметной области – о них позаботится динамический язык Groovy (в частности, он позволяет использовать объекты в качестве прокси при работе с Entity). Вместо этого мы рассмотрим сценарии поиска объектов в базе данных и связи между ними. В настоящее время СУБД NoSQL и различные инфраструктуры не предоставляют встроенных возможностей для реализации подобной функциональности.

Базовый класс Model

Мы начнем с создания базового класса, объекты которого будут хранить экземпляры Entity. Его дочерние классы должны иметь динамический набор свойств, которые будут добавляться в соответствующий экземпляр Entity при помощи удобного метода setProperty в Groovy. Этот метод вызывается при установке значения каждого свойства, для которого нет set-метода (это может показаться странным, но не беспокойтесь – скоро все встанет на свои места).

В листинге 2 показан первый вариант класса Model демонстрационного приложения.