УДК 004.4, 004.65
ИНТЕГРАЦИЯ SQL-ОРИЕНТИРОВАННЫХ СУБД И NOSQL-СИСТЕМ НА УРОВНЕ
ОБЪЕКТНОГО ОТОБРАЖЕНИЯ1
Посконин Андрей Владимирович, аспирант кафедры Системного программирования факультета Вычислительной математики и кибернетики Московского государственного университета им. М. В. Ломоносова, Россия, Москва, [email protected]
Современные приложения вынуждены работать с данными различной структуры, объема и степени важности, предъявляя к системам хранения данных достаточно жесткие (а порой и невыполнимые) требования. Отсутствие универсального решения для работы с данными привело к появлению на рынке большого числа узкоспециализированных систем, значительно отличающихся от традиционных SQL-ориентированных СУБД. Этот новый класс систем получил собирательное название «NoSQL» («Not Only SQL»), подчеркивая тем самым, что в ряде задач применение реляционных СУБД может быть не самым эффективным решением.
Стремительное развитие Интернет и Web-приложений вывело на первый план проблему масштабируемости при возрастающих нагрузках и объемах информации, которую NoSQL-системы стараются решить с использованием распределенных архитектур в противоположность классическим «односерверным» СУБД. Однако поддержка согласованности данных (и, особенно, поддержка ACID-транзакций) в условиях распределенности увеличивает не только сложность системы, но и время отклика, из-за большого числа сетевых взаимодействий. По этой причине NoSQL-системы ослабляют гарантии согласованности данных и отказываются от ACID-транзакций в пользу масштабируемости, скорости и высокой доступности данных (см., например, [1]). Системы категории NoSQL, кроме того, основаны на нереляционных моделях данных (например, ключ-значение, документная модель и т.д.), ориентированы в основном на работу с неструктурированными или слабоструктурированными данными и используют отличные от SQL языки запросов и интерфейсы доступа к данным. Стоит также упомянуть о появлении нового поколения SQL-ориентированных СУБД, изначально создаваемых для работы в распределённой среде, - так называемых NewSQL-системах. Эти СУБД поддерживают транзакционную семантику и язык SQL, но обеспечивают при этом приемлемую масштабируемость. Подробные обзоры существующих NoSQL- и NewSQL-решений можно найти, например, в [2-4].
1 news 4
t id INT
published DATETIME
О text TEXT
L )
—H
1 comments
t id INT ■^newsld INT published DATETIME <> author V ARCHAR(60) text TEXT
1
{
"Jd": Objectld("4cD2c58de5OOfie1be1C0OOO5"], "published": I SQDate["2014-04-01 T00:00:00.00QZ"), "text”:". ",
"comments": [
{
"author": "userl",
"published": ISODatef. ")r "text"
{
"author": "user2",
"published": ISODatef "text":
>
]
}
Рис. 1 - Реляционная и документная модели данных
1 Статья рекомендована к опубликованию в журнале "Информационные технологии"
33
Богатый выбор различных систем для управления данными обусловлен тенденцией к специализации инструментов: если раньше SQL-ориентированные системы рассматривались как универсальные СУБД, способные решать практически любые задачи, то сегодня для каждой задачи можно выбрать наиболее оптимальное и удобное решение (см., например, [5]). Например, если приложению приходится работать с сущностями, обладающими переменным набором атрибутов, то практичнее использовать NoSQL-системы с документной моделью данных, чем реализовывать модель Entity-Attribute-Value [6] средствами SQL. Документная модель данных позволяет хранить объекты с произвольным набором атрибутов, обычно представляемые в JSON и называемые «документами». При этом обычно допускаются списки и вложенные документы, поддерживаются индексы и выборки на основе полей документов. Сами документы обычно хранятся в коллекциях, не накладывающих каких-либо ограничений на набор атрибутов входящих в них документов (отсутствие строгой схемы данных). Одной из наиболее популярных систем такого типа является MongoDB. Эта система имеет богатую функциональность и поддерживает атомарные операции на уровне одного документа, однако если приложение нуждается в полноценных ACID-транзакциях, то SQL-ориентированные системы по-прежнему вне конкуренции.
Термин «Polyglot Persistence» [7] применяется в англоязычных источниках для обозначения практики использования нескольких различных систем хранения данных в рамках одного приложения. Потребность в этом обусловлена тем фактом, что современные приложения (а особенно Web-приложения) вынуждены работать одновременно с несколькими видами данных.
Например, это может быть информация о клиентах, каталог товаров, журналы событий и переходов по ссылкам, данные пользовательских сессий и т.д. Все эти данные не только обладают различной структурой и соотношением операций чтения/записи, но и предъявляют разные требования к надежности хранения и возможностям языка запросов. Таким образом, естественно применить для работы с этими данными несколько систем хранения (см., например, [8-9]):
Рис. 2 - Polyglot Persistence
Однако применение такого подхода усложняет приложение, требуя использования различных языков запросов и интерфейсов для доступа к данным. Одним из решений этой проблемы является применение сервис-ориентированной архитектуры (Service-Oriented Architecture, SOA). При этом все детали доступа к данным эффективно скрываются за интерфейсом соответствующего сервиса, позволяя, к тому же, разбить приложение на более или менее независимые части, что упрощает управление разработкой. Этот подход, однако, требует значительных дополнительных усилий и накладных расходов на организацию взаимодействия между сервисами, что делает его применение нецелесообразным для
34
небольших приложений.
При разработке приложений, использующих системы хранения данных, практически всегда возникает проблема, известная как «impedance mismatch», а именно необходимость каким-либо образом отображать объектную модель приложения на модель данных целевой системы хранения. В случае реляционных СУБД существует множество готовых решений и подходов для осуществления объектно-реляционного отображения (Object-Relational Mapping, ORM). Однако и для NoSQL-решений бывает необходимо осуществлять подобное отображение: например, объектно-документное отображение (Object-Document Mapping, ODM).
Применение готовых решений для объектного отображения, таких как ORM-библиотеки, упрощает и ускоряет разработку приложений, предоставляя высокий уровень абстракции и богатую функциональность. К сожалению, высокий уровень абстракции заметно снижает производительность и делает практически невозможными низкоуровневые оптимизации под конкретную используемую СУБД. По этой причине подобные решения редко применяются в высоконагруженных приложениях, где предпочтительнее использование более оптимизированных под конкретную задачу реализаций (см. [10]).
Слой объектного отображения представляется подходящим для интеграции разнородных систем хранения данных, однако нужно постараться избежать недостатков, присущих традиционным библиотекам объектного отображения. Для этого требуется пересмотр классической архитектуры ORM-библиотек, а именно:
• Использование модульного подхода вместо монолитной системы: слой объектного отображения может быть собран из отдельных «строительных блоков», чтобы лучше отвечать потребностям разработчика. Кроме того, если стандартные реализации компонентов по каким-то причинам не подходят, они должны быть легко заменяемыми.
• Максимально «чистая» объектная модель, свободная от логики взаимодействия с подсистемами хранения. Сущность, в отличие от случая ORM, может содержать не только поля простых типов, но также списки, вложенные объекты и динамические свойства.
• Настраиваемый уровень абстракции: поддержка интерфейсов разных уровней
позволяет найти нужный компромисс между уровнем абстракции и производительностью.
• Независимое отображение сущностей и поддержка ассоциаций поверх слоя отображения позволяет иметь целостную объектную модель, несмотря на то, что сущности могут отображаться на разные системы хранения.
• Наличие унифицированного языка запросов с поддержкой базовой функциональности, не привязанного к SQL или другому языку запросов.
• Возможность усилить гарантии целостности данных с помощью валидации на стороне приложения.
• Поддержка кэширования, «ленивой» загрузки, Unit of Work и других успешно себя зарекомендовавших практик (см., например, [11]).
Приведенные принципы были применены при разработке прототипа программного каркаса MapperStack для интеграции разнородных систем хранения данных на уровне объектного отображения в Web-приложениях на PHP. В архитектуре MapperStack можно выделить два основных слоя: слой отображения, состоящий из объектов, реализующих интерфейс Mapper (например, методы getEntityId(), frnd(), findByQ и т.д.) и верхний слой MapperStack, координирующий работу слоя отображения и предоставляющий возможности интеграции, в том числе поддержку ассоциаций:
35
Рис. 3 - Отображение сущностей с помощью MapperStack
Каждый объект в слое отображения ответственен за отображения сущностей определённого класса. Каждая сущность должна иметь уникальный идентификатор (аналог первичного ключа) и может содержать динамические свойства, списки и вложенные объекты. Сущность может содержать метаданные, определяющие правила отображения, например:
class BlogPost extends MapperStack\Object {
protected $id; protected $username; protected $text; protected $tags; protected $comments;
public static function meta(EntityMetaClass $metaClass)
{
$metaClass->db('blog')
->collection('posts')
->field('id', 'integer', ' id')
->field('username', 'string')
->field('text', 'string')
-field ( 'tags' , 'string[]')
->obj ect('comments', 'Blog\Comment[]')
->id('id');
}
}
В настоящее время реализовано отображение сущностей на MySQL и MongoDB, а также унифицированный язык запросов с трансляцией в SQL и запросы к MongoDB. Поддерживаются операции filter, sort, skip, limit, project и др., условия выборки задаются в виде логического выражения, поддерживаются предикаты (как механизм расширения языка запросов), связываемые и встроенные параметры. Кроме того, если целевая система хранения данных поддерживает документную модель данных, то в запросах можно использовать операции сопоставления элементов коллекции и переход по графу объектов, чтобы добраться до атрибутов вложенных объектов.
Рассмотрим пример запроса в терминах сущностей и результирующие запросы к целевым системам хранения данных:
$p = $m->query('Domain\Product')
->filter('price >= 1000 && type == "shoes"')
->scrt('price', 'asc')
->limit(3)
->getResult();
36
Этот запрос допускает трансляцию как (сопоставление по образцу):
SELECT * FROM 'products'
WHERE 'price' > 1000 AND 'type' = 'shoes'
ORDER BY price'
LIMIT 3
в SQL, так и в язык запросов MongoDB
db.products.find( {
$and : [
{ 'price' : { $gte : 1000} },
{ 'type' : 'shoes' }
] } ).scrt( { 'price' : 1 }
).limit(3);
Рассмотрим теперь запрос, содержащий операцию сопоставления элементов массива (в данном случае в массиве comments ищется элемент, для которого выполняется условие в фигурных скобках, кроме того, присутствует предикат регулярного выражения). Такой запрос не может быть выражен средствами SQL, но поддерживается в MongoDB:
$posts = $m->query('Blog\BlogPost')
->filter('regexp(text, "/programming/i" &&
comments {username == "ivan"} || rating > 100')
->getResult() ;
Результирующий запрос к MongoDB будет выглядеть следующим образом (можно отметить, что запрос в MapperStack является более наглядным и привычным):
db.posts.find( { $or : [
{ $and : [
{ 'text' : /programming/i },
{ 'comments' :
{ $elemMatch: { 'username' : 'ivan' } }
}
] },
{ 'rating' : { $gt : 100 } }
] } );
Для непосредственной организации отображения в MapperStack реализован ряд компонентов, таких как классы для хранения метаданных, адаптеры для систем кэширования, абстракция отображения типа, механизмы для отслеживания изменений и преобразования данных, Unit of Work (откладывание всех операций модификации данных до вызова метода flush(), синхронизующего состояние с системой хранения и выполняющего операции с учетом ассоциаций [12]) и ряд других компонентов. Каждый компонент может быть легко заменен другой реализацией, кроме того, возможны полностью пользовательские реализации классов отображения. Дальнейшее развитие данного подхода может включать поддержку других типов систем, реализацию оптимистических блокировок, абстракцию MapReduce, поддержку модификации без чтения, агрегатных функций, наследования и т.д.
В заключение рассмотрим некоторые аспекты производительности библиотеки MapperStack на примере модуля объектно-реляционного отображения (SQLMapper). Для этого был разработан тестовый сценарий, включающий выборку сущностей, модификацию полей, удаление сущностей и фиксирование сделанных изменений в базе данных. В качестве эталона приведены характеристики для того же сценария, реализованного с помощью библиотеки Doctrine ORM - мощной и хорошо оптимизированной библиотеки объектнореляционного отображения для PHP:
Таблица 1. Результаты исполнения тестового сценария
Время работы (мс) Пиковое потребление памяти (Кб)
MapperStack SQLMapper (первый запуск) 1625 10190
MapperStack SQLMapper (дальнейшие запуски) 595 6940
Doctrine ORM (первый запуск) 1540 9472
Doctrine ORM (дальнейшие запуски) 560 6144
37
При первом запуске сценария происходит анализ и трансляция запросов с последующим кэшированием, чем и обусловлено несколько большее время работы. Кэш результатов запросов при тестировании не использовался. В плане производительности SQLMapper незначительно уступает специализированным ORM-библиотекам (главным образом, вследствие большей модульности и слабой связанности компонентов программного каркаса). Стоит заметить, что с использованием компонентов MapperStack можно реализовать и более производительное отображение (например, работая напрямую с драйвером СУБД, пропуская этап преобразования типов и т.д.).
Реализация модульного программного каркаса для интеграции различных систем хранения на уровне объектного отображения позволяет быстро разработать прототип приложения, используя стандартную функциональность, а затем при необходимости постепенно заменять их на более эффективные реализации исходя из решаемых задач. Интеграция различных систем хранения открывает новые возможности, позволяя использовать наиболее подходящие решения для работы с данными, сохраняя при этом целостную объектную модель приложения.
Литература
1. D. Merriman, «On Distributed Consistency», 2010.
(http://blog.mongodb.org/post/475279604/on-distributed-consistency-part-1)
2. R. Cattell, «Scalable SQL and NoSQL Data Stores», 2011.
(http://www.cattell.net/datastores/Datastores.pdf)
3. 451 Research, «NoSQL, NewSQL and Beyond: The drivers and use cases for database alternatives», 2011.
4. С. Д. Кузнецов, А. В. Посконин, «Распределенные горизонтально масштабируемые решения для управления данными», Труды Института системного программирования РАН, т. 24, стр. 327-358, 2013.
5. M. Stonebraker, U. Qetintemel, «"One Size Fits All": An Idea Whose Time Has Come and Gone», ICDE ’05: Proceedings of the 21st International Conference on Data Engineering, Washington, 2005.
6. Wikipedia: «Entity-Attribute-Value model».
(http://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value model)
7. M. Fowler, «Polyglot Persistence», 2011.
(http://www.martinfowler.com/bliki/PolyglotPersistence.html)
8. С. Д. Кузнецов, А. В. Посконин, «Возможно ли сотрудничество SQL и NoSQL?», Открытые системы. СУБД, № 9, стр. 38-41, 2013.
9. S. Francia, J. Hileman, «Augmenting RDBMS with MongoDB for eCommerce».
(http://www.nosqldatabases.com/main/2011/4/11/augmenting-rdbms-with-mongodb-for-
ecommerce.html)
10. А. В. Посконин, «Web-приложения и данные: проблемы абстракции и масштабируемости», Труды Института системного программирования РАН, т. 23, стр. 159-171, 2012.
11. J. Miller, «Design Patterns for Data Persistence», 2009.
(http://msdn.microsoft.com/en-us/magazine/dd569757.aspx)
12. M. Fowler, «Patterns of Enterprise Application Architecture», Addison Wesley, 2002.
УДК 004.451.8
ОБЪЕКТНО-ОРИЕНТИРОВАННЫЕ ОПЕРАЦИОННЫЕ СРЕДЫ
Штанюк Антон Александрович, к.т.н., доцент кафедры информационных технологий в предпринимательской деятельности, Нижегородский государственный университет им. Н.И. Лобачевского, Россия, Нижний Новгород, [email protected]
Введение
38