Архитектура, основанная на контейнерах
Архитектура, «основанная на контейнерах», ориентирована на использование на стороне клиентов. В ней используется стандартная или проприетарная реализация NFS для доставки по сети сегментов диска, называемых контейнерами, клиентам, в которых реализуется большинство функциональных возможностей ООСУБД. Пользовательское приложение компонуется с клиентской библиотекой ООСУБД, которая обеспечивает кэширование контейнеров, обработку запросов, управление транзакциями и жизненным циклом объектов. Каждый объект должен содержаться внутри некоторого контейнера, и любой контейнер может содержать много объектов; может использоваться несколько контейнеров. В этой архитектуре разработчики приложений для обеспечения доступа к объектам базы данных должны основывать модели прикладного уровня на модели контейнеров.
Рис. 1. Архитектура, основанная на контейнерах
Архитектура, основанная на объектах
Архитектура, «основанная на объектах», является сбалансированной конструкцией, в которой кэширование и поддержка функционирования осуществляется как в процессах приложений, так и в серверных процессах. Серверный процесс кэшируют страницы диска и управляет индексами, блокировками, запросам и транзакциями. Код пользовательского приложения компонуется с клиентскими библиотеками ООСУБД, обеспечивающими кэширование объектов, локальные блокировки и управление жизненным циклом объектов. Не требуется реализация каких-либо стратегий размещения объектов.
Рис. 3. Архитектура, основанная на объектах
Архитектура, основанная на страницах
Архитектура, «основанная на страницах», ориентирована на использование на стороне клиентов; в этой архитектуре выполняется серверный процесс, обрабатывающий запросы страниц в модели отображения распределенной виртуальной памяти. Серверный процесс используется для доставки по сети страниц диска, которые отображаются в виртуальную память приложения, где реализуется основная часть функциональных возможностей ООСУБД. Код пользовательского приложения компонуется с клиентской библиотекой ООСУБД, обеспечивающей кэширование страниц, обработку запросов, управление транзакциями и жизненным циклом объектов. Требуется реализация специальных стратегий размещения объектов.
Рис. 2. Архитектура, основанная на страницах
Архитектуры ООСУБД. Анализ реализаций
Роберт Грин, вице-президент компании Versant
Перевод:
Оригинал: OODBMS ARCHITECTURES. An examination of implementations
Базовая архитектура
Здесь представлены три известные архитектурные реализации ООСУБД. В каждой из реализаций ООСУБД обеспечивают распределенность, параллельную обработку и удаленный доступ, используемые для создания развитых конструкций на уровне системы, но эти системы основываются на разной базовой архитектуре. В этой статье мы будем называть эти три архитектуры основанной на контейнерах (container based), основанной на страницах (page based) и основанной на объектах (object based), что соответствует как единице передачи по сети, так и наиболее низкому уровню гранулированности блокировок. В каждой из архитектур поддерживается кэширование, обработка запросов, управление транзакциями и жизненным циклом объектов (отслеживание новых, недействительных и удаленных объектов). Взглянем на эти архитектуры более внимательно.
Еще раз об архитектурах ООСУБД
Адриан Мариотт, ведущий консультант компании Progress Software
Перевод:
Оригинал: OODBMS Architectures Revisited
Эта статья является ответом на статью вице-президента компании Versant Роберта Грина
Физическая идентификационная информация
Смысл физической идентификации состоит в том, что у всех объектов в системе распределенных баз данных имеется уникальный идентификатор, зависящий от физического месторасположения объекта в системе. Характеристиками физической идентификации являются изменчивость, повторная используемость, стационарность, жесткость.
Одним из последствий является то, что объекты трудно перемещать в системе без влияния на другие объекты, с которыми они связаны. Причина этого состоит в том, что за связями объектов скрываются идентификаторы связанных объектов. Если некоторый объект перемещается в системе, то изменяется его физическое местоположение, и поэтому изменяется его идентификатор (изменчивость). Нужно найти все объекты, существующие в системе, которые содержат ссылку на данный объект, и сделать так, чтобы в полях связи скрывалось новое физическое местоположение (стационарность). По мере возрастания размеров базы данных эта операция становится все более дорогостоящей. Умышленного перемещения объектов можно избежать, хотя имеется много развитых архитектур систем, в которых можно было бы использовать перемещение объектов для управления активными наборами данных в высокопроизводительных крупномасштабных системах.
Но, к сожалению, перемещение объекта может происходить в качестве побочного эффекта других операций, не предусмотренных в архитектуре системы. Например, при эволюции схемы может измениться компоновка объекта, так что он перестанет помещаться в существующем месте. В таком случае эволюция схемы сделает необходимым перемещение объекта и в качестве побочного эффекта вызовет огромную нагрузку на систему, приведя ее в состояние неработоспособности на время изменения схемы. Такую же ситуацию может вызвать увеличение размеров объекта по причине наличия полей переменной длины. К подобной ситуации может привести и изменение схемы кластеризации.
К числу других последствий относится возможное падение производительности вследствие фрагментации. В реляционных базах данных это классическая проблема приложений, производящих большое число удалений, из-за чего производительность с течением времени постоянно деградирует.
Инициализация заново создаваемого элемента данных на основе существующего элемента
В C++ запрещается наличие в одном адресном пространстве двух одноименных классов с разными схемами организации памяти. Однако в данном случае оказывается необходимым именно это, нужен доступ к старым данным при создании нового объекта. Имеется несколько способов достижения желаемого результата, один из которых используется в инструментальном средстве эволюции схем ObjectStore и состоит из трех шагов.
Произвести эволюцию схемы для добавления нового элемента данных. Инициализировать его по умолчанию нулем. На этом шаге не удалять старый элемент данных. Запустить относительно простую пользовательскую программу, которая находит в базе данных все экземпляры с измененной схемой и инициализирует новый элемент данных значением старого элемента данных. Здесь имеется несколько вариантов:
использовать объектный курсор; производить навигацию по специализированным структурам данных приложения; выполнять это действие инкрементно, при первом обновлении объекта; объединить действие с шагом 1 процесса эволюции путем использования функции преобразования, которая будет автоматически вызываться в процессе эволюции схемы для каждого встреченного кандидата на эволюцию.
Последний шаг заключается в запуске процесса очистки, удаляющего старый элемент данных; эта очистка может производиться в любое удобное время, поскольку с логической точки зрения это не требуется, а лишь позволяет освободить память.
Изменение классификации экземпляров
Изменение классификации означает, что некоторый существующий листовой класс (не имеющий подклассов) подвергается рефакторингу путем введения нескольких подклассов. Каждый существующий экземпляр назначается в один из подклассов на основе критерия, задаваемого приложением. Этот процесс снова состоит из трех шагов.
Во-первых, пользователь добавляет в свою схему новые подклассы. Каждый новый подкласс просто включает базовый класс без добавления к нему чего бы то ни было. Определение базового класса не изменяется. Далее пользователь пишет и запускает программу, которая анализирует каждый экземпляр базового класса, выбирает некоторый подкласс и изменяет тип экземпляра, используя вызов функции ObjectStore изменения типа; в этот момент не производятся никакие изменения персистентной схемы объекта, так что все указатели, ссылающиеся на объект с измененным типом, остаются действующими. Наконец, базовый класс и подклассы изменяются в соответствии с их целевыми определениями. Этот шаг может включать удаление элементов данных в базовом классе и добавление некоторых из них в качестве элементов данных подклассов. После компиляции новой схемы пользователь запускает процесс эволюции схемы.
Многие сценарии эволюции схемы могут быть обслужены путем непосредственного вызова инструментального средства эволюции схем ObjectStore. Это средство обеспечивает нахождение всех объектов, их изменение, перемещение их при необходимости в другие страницы и исправление значений и типов указателей. Подробности сценариев, для которых не требуется писать код на C++ для эволюции схемы, лучше всего описаны в руководстве ObjectStore Advanced C++ API User Guide [2], к которому отсылаются заинтересованные читатели.
В тех случаях, когда от пользователей требуется написание некоторого кода на C++, этот код является относительно простым и обычно включает нахождение в базе данных всех эволюционирующих объектов и вызов некоторой функции ObjectStore или инициализацию элементов данных. В любом случае обычно код является достаточно прозаичным. Перемещение эволюционирующих объектов в новые области памяти, если у них изменяется размер, и исправление значений и типов указателей обеспечивается средствами библиотеки эволюции схем.
Здесь фокус состоит в том, что эволюция схемы в ObjectStore действительно является сложной с инженерных позиций, но большая часть этой сложности скрывается от пользователя в утилите эволюции схемы базы данных, поставляемой вместе с продуктов.
Литература
Versant Object Database Fundamental Manual - release 7.0.1.0, July 2005
Objectivity Inc. Technical Overview, Release 9, January 2006 Locking and Concurrency, Objectivity Inc, 1999
ObjectStore Architecture Introductory, Dirk Butson.
См. также ответ на эту статью:
Адриан Мариотт, ведущий консультант компании Progress Software
Логическая идентификационная информация
Смысл логической идентификации состоит в том, что у каждого объекта распределенной базы данных имеется уникальный идентификатор, не зависящий от физического местоположения объекта в системе. Характеристиками логических идентификаторов являются неизменчивость, отсутствие повторного использования, мобильность, гибкость. Последствием является то, что объекты могут свободно перемещаться в системе без влияния на другие объекты, с которыми они связаны. Объект может перемещаться из одной физической базы данных в другую, и существующие связи от других объектов остаются работоспособными, а существующий код по-прежнему может выполняться, поскольку они основаны на логической идентификационной информации, а не на физических адресах. Возможны развитые разработки, такие как архивация данных с использованием активных наборов данных, без потребности наличия в приложениях кода, связанного с местоположением данных. Внутренние операции, подобные операциям эволюции схемы и уплотнения страниц, могут выполняться в режиме он-лайн без риска долговременной потери работоспособности системы. Кроме того, использование логических идентификаторов позволяет обеспечить прозрачную для приложений федерацию находящихся на дисках баз данных как логическую сущность, что дает возможность масштабирования в диапазонах терабайт или петабайт без потребности специального кодирования, связанного с распределенностью данных.
Модель параллельности
Как описывается ниже, у этих трех базовых архитектур имеются разные модели управления параллельным доступом. Модели управления параллельным доступом решают проблему изоляции транзакций и тесно связаны с поведенческими характеристиками сети. В любой архитектуре имеются способы ослабления блокировок для некоторых типов приложений, для которых не требуется полный набор свойств ACID (Atomicy – атомарность, Consistency – согласованность, Isolation – изоляция, Durability – долговечность). Дальнейшая часть этого раздела фокусируется на поведении, требуемом для поддержания корректными транзакциями строгих ACID-свойств согласованности и изоляции. Во всех реализациях блокировки могут локально кэшироваться на клиенте за пределами границ транзакций во избежание избыточных запросов блокировки. Однако обновления всегда приводят к выполнению операций координации блокировок и согласования кэшей. При использовании архитектур, основанных на контейнерах и страницах, размещение объектов и блокировки тесно связаны, а архитектура, основанная на объектах, позволяет ортогонализировать эти аспекты. Взглянем более пристально на эти аспекты каждой из архитектур.
Однако Грин не упоминает о том, что в системах реального мира блокировки на уровне объектов требуют дорогостоящего управления и часто бывают избыточными с точки зрения реализуемого сценария использования. В большинстве алгоритмов до успешной фиксации транзакции требуется доступ к ряду объектов, так что наличие гибкой возможности заблокировать их при надобности одновременно, а при другой потребности – независимо один от другого может быть выигрышным с точки зрения производительности.
В системах с архитектурой, основанной на страницах, следствием размещения объектов являются одновременная блокировка объектов, расположенных в одной и той же странице, но являются ли эти блокировки паразитными, или вредными в каком-либо отношении, зависит от деталей сценария использования. Если имеются два объекта O1 и O2, расположенные в одной и той же странице, и некоторая программа выполняет транзакцию, в которой обновляются оба эти объекта, то одновременная блокировка не создает никаких проблем. На самом деле, от нее исходит преимущество, поскольку сокращаются накладные расходы на управление блокировками.
Грин также не упоминает о том, что в системах с архитектурой, основанной на страницах, при наличии реальной потребности в блокировке на уровне объекта можно просто поместить объект на отдельную страницу. Если требуется одновременный и независимый доступ к объектам O1 и O2 от разных программ, при использовании ObjectStore программист может позаботиться об их размещении в разных страницах, чтобы они блокировались независимо.
В ObjectStore обеспечивается очень простой и гибкий API, делающий возможным расположение в одном наборе страниц объектов, используемых совместно в одном и том же сценарии использования, и расположение в других страницах объектов, для которых требуются индивидуальные блокировки. Поэтому в течение проектирования и реализации системы, основанной на ObjectStore, программист может сгруппировать персистентные данные в разных областях базы данных, называемых кластерами, таким образом, что данные занимают минимально возможное число страниц.
Основные факторы
Имеется несколько ключевых реализационных различий, которые приводят к существенно разным характеристикам систем во время выполнения. Задачей этой статьи является обсуждение не всех функциональных возможностей, а только тех основных областей, которые влияют на производительность и масштабируемость при разных характеристиках приложений. В число этих областей входят базовая архитектура, модель параллелизма, реализация запросов, сетевая модель и управление идентификационной информацией.
Ответ на комментарии к разделу «Физическая идентификационная информация»
По поводу утверждения Мариотта, что я пренебрегаю «очень быстрым» разыменованием физических идентификаторов хочу сказать следующее. Я задаю вопрос и призываю задать этот вопрос читателей: «как можно назвать реализацию «очень быстрой», если сначала нужно все загрузить в клиента?». В Versant можно зафиксировать объекты в памяти и пользоваться обычными указателями по памяти, никак не заботясь об идентификаторах базы данных. Как я уже говорил, я считаю, что навигация хорошо поддерживается во всех ООСУБД, но я не согласен с позицией Мариотта по данному вопросу, и, возможно, мне следует обсудить аспект навигации более подробно.
Пусть читатели статьи Мариотта сами решают, насколько гибкой является эволюция схемы в среде ObjectStore. Я не являюсь экспертом по реализации ObjectStore, и Мариотт много написал, чтобы показать читателям, что эволюция схемы в ObjectStore возможна. Я уже высказал свое мнение об этом в исходной статье.
Ответ на комментарии к разделу «Следствия модели запросов»
Хочу пояснить свое отношение к навигационному доступу, которому, по утверждению Мариотта «уделяется недостаточное внимание». Цель исходной статьи состояла в том, чтобы подчеркнуть основные различия между ООСУБД, и, если не учитывать различия в семантике реализации, навигацию все они поддерживают очень хорошо. Под этим я имею в виду то, что во всех реализациях эффективная навигация достигается в основном за счет статически определенных связей, а не динамически определяемых связей (вычисляемых соединений), как в реляционной технологии. Я не вижу смысла в дальнейшем обсуждении этой темы, поскольку она полностью раскрывается в руководствах всех поставщиков.
На мой взгляд, оспаривание важности модели запросов в части высказывания Мариотта «:приводит к вопросу о следствиях модели запросов в целом» показывает серьезное отсутствие понимания значимости этого механизма для эффективного запуска сценария использования. Модель запросов, ее реализация, ее эффективность становится очень существенной при возрастании объемов наборов данных, с которыми имеет дело приложение. Отсутствие понимания этого факта служит в поддержку моего утверждения, что архитектура, основанная на страницах, наименее подходит для использования в приложениях, работающих с большими объемами данных. Не считая этого, большая часть материала этого раздела является хорошим обсуждением значимости навигации ВО ВСЕХ ООСУБД.
Кажется, я действительно был не прав, утверждая, что НЕТ НИКАКОЙ ВОЗМОЖНОСТИ доступа к объектам, не содержащимся в коллекциях, над которыми можно выполнять запросы. Правильнее было бы сказать, что, как кажется, в ObjectStore можно загрузить в клиента все объекты некоторого класса и последовательно сканировать их, пока не найдутся искомые объекты. Однако в практической, а не теоретической реальности, несмотря на наличие «возможностей», описанных Мариоттом в этом разделе, если попытаться инсталлировать сегодняшнюю реализацию архитектуры, основанную на страницах, и сразу воспользоваться ею, вы не сможете выполнять запросы на стороне сервера, поскольку для этого придется загрузить в клиентов много сторонней информации.
Ответ на комментарии к разделу «Управление идентификационной информацией»
Хочу сделать замечание относительно возможности масштабирования за счет лишь поддержки 64-разрядных машин. Проблема масштабируемости проистекает не только из использования физической идентификационной информации, а и из особенностей реализации. Я соглашусь с тем, что в 64-разрядной реализации архитектуры, основанной на страницах, можно достичь большего уровня масштабируемости. Но я не думаю, что путем перекомпиляции всех приложений в расчете на 64-разрядную машину можно повысить их масштабируемость. Возможно, это связано с тем, что эта проблема является проблемой не архитектуры, основанной на страницах, а конкретной реализации. Если взглянуть на сегодняшние приложения, работающие с терабайтными объектными базами данных, то поддерживающими их ООСУБД преимущественно окажутся Objectivity and Versant. У этого факта имеется причина, и хотя теоретически 64-разрядная адресация позволяет работать с более крупными наборами данных, я продолжаю настаивать на своем практически обоснованном утверждении, что реализации архитектуры, основанной на страницах, не очень подходят для работы с очень большими наборами данных. ООСУБД Versant и Objectivity обеспечивали возможность работать с данными подобного объема намного раньше появления 64-разрядных машин.
Ответ на комментарии раздела «Архитектура, основанная на страницах»
Похоже, что проблему Мариотту создала часть предложения «*Требуется* реализация», касающаяся стратегий размещения объектов в ObjectStore. Снова замечу, что я старался привести практические, а не теоретические рекомендации. С моей точки зрения, только в наиболее тривиальных демонстрационных версиях систем с архитектурой, основанной на страницах, можно было бы игнорировать проблему размещения объектов. В этой статье я укажу на места, в которых Мариотт снова и снова ссылается на эту стратегию размещения объектов как основное средство устранения различных трудностей разработки. Так что, говоря практически, в реальном мире должно существовать приложение, для которого будет иметься значительное число одновременно работающих пользователей и большие базы данных, и своей статьей Мариотт подтверждает эту точку зрения. Если приверженец технологии ООБД решит игнорировать этот аспект, он пойдет на риск.
Ответ на комментарии раздела «Модель параллельности»
По-видимому, в Objectivity и Versant имеется возможность ослаблять свойства ACID, а в ObjectStore такой возможности нет. Так что я признаю ошибку. Но в любом случае это замечание Мариотта непонятно с учетом того, что в следующем предложении я говорю, что мое обсуждение фокусируется на правильном ACID-поведении. Процитированное предложение «обновления всегда приводят к выполнению операций координации блокировок и согласования кэшей.» приведено Мариоттом вне контекста.
Очевидно, что в моем контексте это означало, что для ТРАНЗАКЦИИ, демонстрирующей свойства ACID и выполняющей ОБНОВЛЕНИЯ, потребуются операции координации блокировок и согласования кэшей. Если мы говорим об индивидуальных операциях внутри транзакции, то имеется множество возможностей. В связи с этим я не понимаю замечания Мариотта о потребности блокировки миллионов объектов и влиянии этого на поведение систем с архитектурой, основанной на объектах. В практической реализации в этих случаях использовался бы паттерн блокировок GateKeeper. Например, если доступ к тысячам атомов, образующим сложную структуру, производится только через эту структуру, то потребуется блокировать структуру, а не атомы. Я вспоминаю ранние дни реляционных баз данных, когда разные поставщики приводили аналогичные аргументы по поводу гранулярности блокировок, а теперь, десять лет спустя все основные поставщики реализуют блокировки на уровне записей. Я не считаю, что эта тема завершена моим примером или примером Мариотта. Однако, вопреки утверждению Мариотта, я не «опускаю» информацию, касающуюся семантики блокировок приложения. Транзакционные блокировки требуются даже для транзакций с оптимистическими блокировками, чтобы они могли получить логически согласованный мгновенный снимок базы данных. Для рассмотрения всех возможностей потребовалась бы целая книга.
Далее автор говорит: «Грин также не упоминает о том, что в системах с архитектурой, основанной на страницах, при наличии реальной потребности в блокировке на уровне объекта можно просто поместить объект на отдельную страницу.». В таком случае я полагаю, что забыл сказать и о бесполезных затратах памяти при хранении 20-байтного объекта в странице размером в 4096 байт, а также о потерях виртуальной памяти при трансляции адресов и потерях пропускной способности сети при передаче почти пустых страниц... А вообще-то, конечно, можно иметь гранулированные блокировки и в архитектуре, основанной на страницах. Я предлагаю читателям самим решить, чем различаются практика и теория.
Ответ на комментарии раздела «Параллельность в архитектуре, основанной на страницах»
По поводу семантики блокировок и следствий для приложений с высоким уровнем параллельного доступа можно сказать следующее. В своей статье Мариотт пишет: «Грин утверждает, что это представляет собой потенциальную проблему. Насколько серьезно это в действительности?». Я полагаю, что это зависит от того, используется или не используется эта «не обязательная» стратегия размещения объектов, и, конечно, на это указывает даже сам автор! Оставшаяся часть этого раздела статьи Мариотта является подробным объяснением того, как на блокировки в ObjectStore влияют CRUD-операции, и как можно использовать «необязательные» кластеризацию и стратегии размещения объектов, чтобы избежать отрицательного воздействия этих операций, но когда Мариотт утверждает, что «в системах реального мира большая часть паттернов доступа соответствует сценарию "в основном чтение"», ему следовало бы подтвердить это примерами из своего практического опыта.
Ответ на комментарии раздела «Реализация
Что касается комментария Мариотта по поводу выполнения запросов в ООСУБД с архитектурой, основанной на страницах, я согласен, что в процитированном автором утверждении не учитывалась возможность индексации, и это является моим упущением. Наверное, следовало бы переформулировать это утверждение, добавив следующее: «При наличии индексации требуется загрузить по сети все страницы индекса, а после выполнения запроса и определения его результатов нужно загрузить по сети все не загруженные ранее страницы, которые содержат результат, чтобы обеспечить доступ к объектам из результирующего набора. Это приведет к пересылке по сети многих объектов, которые в действительности не удовлетворяют условию запроса.» И вопреки утверждению Мариотта я знаю, что в ObjectStore используются индексы, поскольку говорил в подразделе «Следствия модели запросов» о проблемах поддержки индексов в архитектуре, основанной на страницах, считая наличие этих проблем недостатком архитектуры. Судя по комментариям Мариотта, кажется, что он, должно быть, не заметил этого подраздела.
Дальше Мариотт пишет: «Однако следует сравнить это с сервер-ориентированной архитектурой, где все запросы должны выполняться на серверной машине, имеется ограниченная возможность распределения этих запросов и использования истинного параллелизма нескольких машин, когда это имеет смысл.» Конечно, я уже указывал на то, что в этой области Versant не демонстрирует те же характеристики, что реляционные СУБД, но, кроме того, важно заметить, что при выполнении на разных машинах ООСУБД Versant обладает всеми возможностями для работы со многими физическими базами данных как с единой логической сущностью, запросы к которой могут выполняться в параллель. Так что, по-видимому, к этому нельзя относиться как к небольшой опции. Я решусь предположить, что Мариотт просто не знаком с особенностями ООСУБД Versant. Позже в этой статье я вернусь к этой возможности.
Ответ на комментарии раздела «Следствия модели параллельности»
Мне кажется, что здесь Мариотт предполагает использование замечательной стратегии размещения объектов. Когда автор утверждает, что «Грин забыл упомянуть о том, что тот же самый трешинг может возникнуть на уровне объектов в системах, в которых используются блокировки на уровне объектов», он не является корректным. Я не говорил об этом, поскольку, если бы в объектной архитектуре имелся этот конфликт, он бы разрешался за счет поддержки очереди запросов блокировок, и для этого бы не требовались возвратные вызовы всех клиентов, у которых соответствующий объект находится в кэше, как это требуется в сервере с архитектурой, основанной на страницах. Я снова предлагал практическую рекомендацию, иллюстрирующую важность стратегий размещения в архитектуре, основанной на страницах. Именно поэтому далее я писал: «Приложения, у которых имеются относительно фиксированные модели с редко конфликтующими обновлениями, хорошо сегментирующиеся в независимый кластер, обычно ведут себя наилучшим образом в архитектурах, основанных на контейнерах и страницах.» Но эту фразу Мариотт не процитировал.
Далее Мариотт утверждает, что «здесь Грин не упоминает о том, что в сервер-ориентированных системах, таких как Versant, по мере возрастания числа пользователей могут быстро проявиться проблемы масштабируемости, поскольку большая часть обработки должна производиться на сервере», и это полностью неверно. Так ведут себя реляционные системы, в которых для доступа ко всем данным используются только запросы, и в этом основной смысл использования термина «баланс» в моей исходной статье. Реляционные системы делают все (выполняют запросы) в серверном процессе, а в некоторых объектных архитектурах, таких как ObjectStore, используется противоположный подход, и все делается в клиентском процессе. В ООСУБД Versant и ее архитектуре, основанной на объектах, устанавливается некоторый «баланс», и на стороне сервера выполняются запросы, а на стороне клиента поддерживаются навигация и кэширование.
Ответ на комментарии раздела «Следствия сетевой модели»
Здесь в своем первом комментарии автор говорит об использовании этих «не обязательных» стратегий размещения для решения упоминаемой им проблемы. И во втором комментарии оказывается, что спасают именно «не обязательные» стратегии размещения.
Что же касается того утверждения Мариотта, что реализации с архитектурой, основанной на объектах, оказываются не слишком пригодными для приложений с большим числом одновременно работающих пользователей из-за наличия узких мест на стороне сервера и блокировок мелких объектов, я уже пояснял, что в Versant в качестве основного средства доступа к объектам используется навигация, а не запросы. Я также говорил, что в многочисленных публикациях, затрагивающих проблему блокировок мелких объектов, приводится много паттернов блокировок, и даже упомянул про транзакции с оптимистическими блокировками, в которых для достижения согласованности используются временные метки, а не блокировки. Все это говорилось только для обеспечения полной ясности. Я не говорил, что для обеспечения наилучших результатов запросов не обязательно требуется тщательно продумывать их формулировки, заботиться об индексации, оптимизации и т.д. Однако при написании исходной статьи я считал, что читателям известны эти основы методов доступа к базам данных.
Ответ на комментарии «Введения»
Как кажется, здесь Мариотт подвергает сомнению мое утверждение, что архитектуры ООСУБД оказывают огромное влияние на масштабируемость приложений. Очевидно, что в комментариях автора игнорируется контекст, который можно найти в предложении, предшествующем цитате, приведенной в статье Мариотта. В исходной статье наравне с архитектурными особенностями ООСУБД подчеркивалась значимость характеристик приложения, на основе которых, в конце концов, принимаются архитектурные решения, хотя, честно говоря, я считал это очевидным для своих читателей. Я остаюсь верным своему утверждению: ООСУБД демонстрируют очень разные характеристики, и приверженцу этой технологии следует проанализировать характеристики приложения и подобрать архитектуру ООСУБД, наилучшим образом соответствующую его потребностям. С моей точки зрения, продолжающаяся дискуссия о правильном использовании методов объектно-ориентированного анализа должна быть понятна типичному читателю этих статей, и поэтому я считаю неуместными соответствующие высказывания Мариотта в комментариях к моей исходной статье.
Параллельность в архитектуре, основанной на контейнерах
В этой архитектуре параллельный доступ к информации одного контейнера координируется отдельным серверным процессом блокировок. Клиенты взаимодействуют с централизованным процессом блокировок для запросов блокировок контейнеров в каждой базе данных серверной машины до того, как кэшировать контейнер на клиенте путем обращения к серверу страниц NFS. Запрос на обновление ставится в очередь запросов блокировок контейнера, если к этому времени имеются удовлетворенные блокировки контейнера по чтению. Этот запрос либо отвергается по причине превышения максимального интервала времени ожидания, либо удовлетворяется, когда существующие клиенты освобождают свои блокировки. Как правило, блокировки удерживаются в пределах транзакции. После образования очереди по причине поступления запроса блокировки на обновление все последующие запросы блокировок ставятся в очередь вслед за этим запросом. После того, как выполняется обновление, удовлетворяются все стоящие в очереди запросы блокировки на чтение, и, если нет других обновлений, очередь исчезает. Клиенты, кэшировавшие обновленный контейнер, должны при последующем чтении обновить свой кэш. В контейнерах может содержаться много объектов, поэтому возможны ложные ожидания и ложные синхронизационные тупики.
Параллельность в архитектуре, основанной на объектах
В этой архитектуре процесс сервера поддерживает очереди запросов блокировок на уровне объектов для управления параллельным доступом к одному и тому же объекту. Запрос блокировки для обновления образует очередь, если имеются читатели объекта. Этот запрос либо удовлетворяется, когда все читатели освобождают свои блокировки, либо отвергается по истечению предельно допустимого интервала времени. После образования очереди запросом блокировки для обновления все последующие запросы попадают в очередь за этим запросом для обновления. После выполнения обновления и снятия соответствующей блокировки с объекта все запросы блокировок для чтения удовлетворяются, и, если больше нет обновлений, очередь исчезает. Клиенты, кэшировавшие обновленный объект, должны обновить его в кэше при последующем чтении. В этой архитектуре блокировки устанавливаются на уровне объектов, поэтому ложные ожидания и ложные синхронизационные тупики не возникают.
Параллельность в архитектуре, основанной на страницах
В этой архитектуре сервер страниц координирует параллельный доступ путем отслеживания того, какие приложения обращаются к каждой странице, предоставления прав на локальную блокировку страниц и использования возвратных вызовов блокировок. Запрос страницы для обновления вынуждает сервер страниц использовать возвратные вызовы, предлагающие другим клиентам освободить свои блокировки этой страницы и отказаться от права на локальную блокировку этой страницы. Либо все клиенты снимают свою блокировку и отказываются от права на локальную блокировку, и запрос блокировки для обновления удовлетворяется, либо этот запрос блокируется на заданный промежуток времени. Каждый клиент, отказавшийся от своих прав, обновляет страницу в кэше и запрашивает новые права и блокировки для следующего доступа к информации на данной странице. Блокировки и права получаются на уровне страниц, и в каждой странице может содержаться много объектов, так что возможны ложные ожидания и ложные синхронизационные тупики.
В этой ситуации применимы два основных подхода. Наиболее эффективным способом избежать этой проблемы является использование стратегии размещения объектов там, где это требуется. Это может полностью устранить избыточные ожидания и синхронизационные тупики.
Однако возникает сложность при попытке кластеризации объектов в расчете на несколько сценариев использования. У разных сценариев использования могут иметься конкурирующие или противоречивые требования к кластеризации, когда один сценарий использования лучше всего оптимизируется при одном способе кластеризации объектов, а для другого сценария предпочтителен другой способ. Эти сложности действительно возникают в реальных проектах, и имеется несколько подходов к их разрешению.
Один из подходов состоит в игнорировании наименее важного сценария использования и концентрации усилий на наиболее важном сценарии. Этот подход работает, например, в тех случаях, когда основной сценарий использования системы конфликтует с менее важным вторичным сценарием использования, который проигрывается редко. Другой подход заключается в повторном обращении к объектной модели, в особенности, к структурам доступа, и ее изменении с целью оптимизации кластеризации для двух (или более) важных сценариев использования. В этом случае разработчик явно настраивает структуры данных и кластеризацию, чтобы они «содействовали» друг другу во время выполнения. Такая настройка может, например, включать хранение дополнительных структур данных для выполнения второго сценария использования, чтобы при этом не затрагивались объекты с неоптимальной кластеризацией. Имеется много вариантов этого подхода. В третьем подходе нарушается другое неприкосновенное правило проектирования баз данных «отсутствия избыточного хранения данных» (store-it-once), и оптимизируются два важных сценария использования с режимом «только чтения» в ущерб сценариям использования, обновляющим базу данных. В этом случае одни и те же данные кластеризуются двумя разными способами для максимальной оптимизации читающих сценариев использования за счет введения требования записи в две разные структуры данных при выполнении обновления.
Реализация запросов
Реализации ООСУБД фокусируются на обеспечении возможности бесшовной навигации между связанными объектами с использованием языковых конструкций. Прирожденная поддержка паттернов навигационного доступа является ключевым преимуществом ООСУБД над сложным понятием соединения, применяемого в РСУБД. По существу, связи являются статической, а не вычисляемой во время выполнения частью системы, что существенно убыстряет их использование. В каждой реализации теоретически возможно определить один главный объект, выборка которого обеспечивает навигационный доступ ко всем другим объектам базы данных. Однако по практическим соображениям реализации приложений обычно управляются сценариями использования. Нет смысла без нужды затрагивать объекты, не соответствующие сценарию использования. Кроме того, из требований масштабируемости больших баз данных следует потребность в эффективном использовании дисков, сети и основной памяти. Поэтому в ООСУБД обеспечиваются средства запроса запросов на выборку объектов первого уровня, соответствующих сценарию использования, и допускается последующая навигация от этих стартовых объектов. Здесь мы снова не преследуем цели детального объяснения поведения каждого процессора запросов, а обсуждаем основные моменты реализаций. За пределами статьи остаются такие подробности, как возврат идентификаторов для отложенной загрузки, а не реальных объектов, и т.д.
Реализация запросов в архитектуре, основанной на контейнерах
В реализации архитектуры, основанной на контейнерах, используется обработка запросов на стороне клиента. «Клиент» является некоторым процессом, отличным от серверного процесса страниц NFS. Можно сопоставить это с традиционной РСУБД, архитектура которой имеет противоположную природу. В РСУБД все сосредоточено в серверном процессе: обработка запросов, индексация, блокировки и управление страницами, что делает ее реализацию сервер-ориентированной, а не клиент-ориентированной. В реализации архитектуры, основанной на контейнерах, все объекты базы данных, которые затрагиваются запросом, должны идентифицироваться некоторым контейнером базы данных. Этот контейнер содержит все потенциально полезные индексы и загружается в клиентский процесс для выполнения запроса. После загрузки всех потенциально требуемых объектов или индексов и выполнения запроса возвращаемые результаты являются контейнерами, содержащими результирующие объекты, которые удовлетворяют запросу. Тем самым, с точки зрения передачи по сети и блокировки результат может содержать много объектов, которые в действительности не удовлетворяют предикату запроса. Если клиент является удаленным, то результаты попадают в исходные запрашивающий пользовательский процесс через несколько звеньев. Эффективность выполнения запросов может обеспечиваться путем использования общесистемных идентификаторов, многопотоковой, удаленной и распределенной обработки, агрегирования.
В реализации архитектуры, основанной на страницах, используется обработка запросов на стороне клиента. При этой реализации все объекты базы данных, которые затрагиваются запросом, должны содержаться в коллекциях, загружаемых в клиентский процесс для выполнения запроса. Запросы и индексация могут срабатывать только над этими коллекциями. После загрузки коллекции объектов и выполнения запросов результатом являются ссылки на объекты, удовлетворяющие предикату запроса, и неявно уже загруженные по сети и подсоединенные к виртуальной памяти клиента страницы, которые содержат эти объекты. Тем самым, с точки зрения передачи по сети и блокировки результат может содержать много объектов, которые в действительности не удовлетворяют предикату запроса.
В реализации архитектуры, основанной на объектах, используется процессор выполнения запросов, который выполняется в процессе сервера баз данных. Любой объект базы данных достижим через запрос, даже если у него отсутствует связь с другими объектами. Атрибуты объектов могут индексироваться при поддержке сервера баз данных. Запрос производится путем посылки серверу некоторого оператора, который выполняется на сервере с использованием оптимизатора и механизма индексации, и клиенту возвращается результирующий набор объектов. По сети передаются только оператор, который будет выполняться на сервере, и коллекция объектов, удовлетворяющих предикату, в качестве результата запроса. Эффективность выполнения запросов может обеспечиваться путем использования общесистемных идентификаторов, многопотоковой, удаленной и распределенной обработки, агрегирования.
«После загрузки коллекции объектов и выполнения запросов результатом являются ссылки на объекты, удовлетворяющие предикату запроса, и неявно уже загруженные по сети и подсоединенные к виртуальной памяти клиента страницы, которые содержат эти объекты. Тем самым, с точки зрения передачи по сети и блокировки результат может содержать много объектов, которые в действительности не удовлетворяют предикату запроса.»
Это утверждение ложно. Кажется, Грин думает, что в ООСУБД с архитектурой, основанной на страницах, во время выполнения запросов не используются индексы, но это неверно. В ObjectStore индексы разработаны таким образом, что при выполнении запроса клиенту передаются только страницы индексов. Структуры данных индексов являются очень компактными, и они всегда размещаются в собственных страницах базы данных, поэтому они могут эффективно передаваться с сервера, и параллельно выполняемые запросы от разных клиентов могут одновременно обрабатываться на разных машинах.
Если сетевые расходы на пересылку страниц являются слишком обременительными, то путем выполнения клиентов ObjectStore на общей машине с сервером можно полностью устранить сетевые накладные расходы. Сервер по умолчанию отображает в свою виртуальную память кэши всех локальных клиентов, так что пересылка страниц происходит действительно быстро, и все это фактически работает, как единый процесс. Так что путем декомпозиции системы в N-звенную архитектуру можно выполнять клиентские части ObjectStore либо на машинах, удаленных от серверного процесса, либо на той же машине, что и серверный процесс, устраняя, тем самым, сетевые накладные расходы или распределяя обработку по разным машинам в зависимости от особенностей и потребностей конкретного контекста.
Размещение клиентского процесса на серверной машине может устранить накладные расходы на сетевые коммуникации, но не устраняет трешинг страниц, если, например, имеется большое число параллельно выполняемых обновлений или отсутствует индекс для оптимизации запросов. Так что ObjectStore обладает некоторой гибкостью, но не демонстрирует преимущества обработки запросов на стороне сервера во всех возможных случаях.
Однако следует сравнить это с сервер-ориентированной архитектурой, где все запросы должны выполняться на серверной машине, имеется ограниченная возможность распределения этих запросов и использования истинного параллелизма нескольких машин, когда это имеет смысл. Сервер-ориентированная организация может порождать узкие места на стороне сервера при возрастании числа одновременно работающих пользователей, и единственным выходом из положения является приобретение более крупных и мощных серверных машин.
«В реализации архитектуры, основанной на объектах, используется процессор выполнения запросов, который выполняется в процессе сервера баз данных.»
С теоретической точки зрения использование архитектуры, основанной на страницах, не исключает возможности выполнения запросов в процессе сервера баз данных. В статье Грина делается заключение, что в этом проявляется некоторая ограниченность архитектур, основанных на страницах, и его можно преодолеть, только обратившись к архитектуре, основанной на объектах. Но в действительности тот факт, что в некотором продукте ООСУБД обеспечивается обработка запросов на стороне сервера, характеризует только этот продукт и совсем не обязательно связан с тем, реализуется ли в этом продукте архитектура, основанная на страницах или объектах.
Как отмечалось выше, при использовании ObjectStore можно выбрать, где хотелось бы выполнять запросы – на клиентской машине или на сервере. Это значит, что некоторые проблемы узких мест сервера, в частности, те, которые проистекают из конкуренции за центральный процессор, могут быть смягчены путем размещения процессов на других машинах и использования истинного параллелизма.
«Любой объект базы данных достижим через запрос, даже если у него отсутствует связь с другими объектами.»
Прежде всего, хочу сделать небольшое логическое замечание относительно этого утверждения: использование архитектуры, основанной на страницах, не исключает возможности запросов в базе данных произвольных объектов. Имеется теоретическая и практическая возможность нахождения и обработки любого объекта в любом файле системы баз данных. Такую возможность можно, например, реализовать, хотя и не эффективно, путем последовательного перебора объектов всей базы данных. Обеспечивается ли такая возможность, и, если обеспечивается, то насколько эффективно,– это всего лишь характеристика конкретного продукта.
Хотя на первый взгляд у возможности произвольных запросов не видно недостатков, при обеспечении средств, позволяющих запрашивать все что угодно в любой точке программы, возникает ряд проблем.
Одним из наиболее важных свойств объектно-ориентированной парадигмы является инкапсуляция, сокрытие за интерфейсом деталей реализации. По существу, объекты представляют собой неделимые вычислительные «атомы», у каждого из которых имеется контракт с другими объектами или клиентами. Для поддержания этой инкапсуляции состояние элементов данных объекта является или должно являться частным, и при вызове метода, обновляющего объект, реализация объекта действует в соответствии с контрактом, изменяя частное внутреннее состояние таким образом, чтобы постусловия и инварианты, а также другие свойства контракта соблюдались к моменту возврата из метода. Часть этого частного состояния образуется связями между объектами. Поскольку весь код, реализующий контракт, содержится внутри соответствующего класса, и только у этого кода имеется доступ к частным элементам данных, поддержка инкапсуляции в компиляторе в значительной степени способствует соблюдению контакта путем автоматического устранения возможности повреждения внутреннего состояния объекта какой-либо внешней программой.
Такая организация означает, что если требуется что-то изменить, или если происходит что-либо неверное, то достаточно просто обнаружить и устранить ошибку, поскольку возможность доступа к внутреннему состоянию объекта имеется только из кода, локального по отношению к этому объекту. Следует сравнить это со случаем, когда имеются общедоступные элементы данных, доступ к которым возможен из любого места внешней программы. Тогда отсутствует инкапсуляция в упомянутом выше смысле, и легко видеть, что эта поддерживаемая компиляторами инкапсуляция является фундаментальной чертой объектно-ориентированной технологии – очень мощным средством контроля сложности, – а не просто выдумкой борцов за чистоту идеи.
При персистентном хранении объектов на диск записывается именно это частное внутреннее состояние. Разрешение доступа из произвольного кода к внутреннему состоянию любого объекта подрывает инкапсуляцию, позволяя коду, внешнему по отношению к объектному классу, изменять состояние объектов.
Это означает, что состояние любого объекта может быть изменено любой программой. Другими словами, больше не гарантируется, что только из кода методов класса допускается доступ к этому частному состоянию, и, следовательно, компилятор больше не может обеспечивать выполнение контракта объекта. Практическим выводом является то, что возрастает общая связность всей системы, она становится более сложной, усложняется ее поддержка и тестирование. В системе, вероятно, потребуются проверки времени выполнения, вредно влияющие на производительность.
Поэтому, хотя наличие произвольного доступа к любому персистентному объекту может показаться полностью здравой идеей, с теоретических и практических позиций объектно-ориентированного подхода ситуация является более сложной – имеются потенциальные негативные стороны. Следует заметить, что эта проблема идентична случаю, когда объекты сохраняются в реляционных таблицах, что, по существу, и делается во многих реализациях архитектуры, основанной на объектах.
«Запрос производится путем посылки серверу некоторого оператора, который выполняется на сервере с использованием оптимизатора и механизма индексации, и клиенту возвращается результирующий набор объектов.»
Прежде всего, из помещения этого утверждения в подраздел «Реализация запросов в архитектуре, основанной на объектах» и отсутствия упоминания оптимизации запросов в подразделе «Реализация в архитектуре, основанной на страницах» следует, что в системах объектных баз данных с архитектурой, основанной на страницах, во время выполнения запросов не может производиться оптимизация. Конечно, это не так.
В ObjectStore запросы обрабатываются на стороне клиента, и если имеются пригодные индексы, они используются для оптимизации обработки запроса. Само по себе наличие индексов не изменяет способ раздачи страниц базы данных, используемый серверным процессом. В ObjectStore индексная структура данных трактуется точно так же, как и любая другая персистентная структура данных. Когда индекс используется для оптимизации запроса при его обработке на стороне клиента, в серверный процесс посылаются запросы страниц частей самого индекса.
Таким образом, в клиентский процесс доставляются только те части индекса, которые требуются для обработки запроса. Поскольку индексы всегда размещаются в своем собственном специальном наборе страниц базы данных, при использовании индекса клиенту передается минимальное число страниц базы данных – избегаются паразитные и избыточные страничные конфликты. При правильном выборе индексов и наличии должного контекста этот процесс оптимизации может сильно сократить число страниц, запрашиваемых у серверного процесса ObjectStore.
При вызове метода запроса возвращается коллекция указателей на результирующий набор – набор персистентных объектов внутри базы данных. К этим объектам не производится доступ при обработке запроса; поэтому страницы, содержащие эти объекты, не передаются в клиентский процесс.
Здесь нужно отметить еще одну отличительную особенность ObjectStore, повышающую ее гибкость и общую производительность. Эта особенность основывается на идее специальной индексации. В архитектурах, в которых запросы выполняются на стороне сервера, индексы, естественным образом, используются на стороне сервера, но это обычно означает, что реальная структура этих индексов определяется поставщиком базы данных, а не пользователем. Обработка запросов на стороне сервера ограничивает возможность определения структур данных пользователем, реально использующим индекс. Поскольку в ObjectStore разрешается хранение реальных указателей и истинных массивов C++, программист может реализовать эффективные специальные индексные структуры, которые оптимально поддерживают конкретный сценарий использования. Если имеется несколько сценариев использования, для которых требуется максимальная производительность, то на целевые объекты могут ссылаться специальные индексы, специфичные и оптимизированные для каждого сценария использования. При этом подходе в базе данных различаются две основные группы объектов: бизнес объекты, в которых сохраняются реальные данные, соответствующие моделируемой предметной области, и заключается весь смысл системы; и структуры доступа, или индексы, которые поддерживают навигацию к бизнес-объектам, соответствующим конкретным вызовам сценариев использования, или запросам.
Программисты могут реализовывать оптимальные структуры данных для обработки объектов в базе данных, используя например, даже STL (Standard Template Library), точно так же, как если бы они являлись структурами данных в «куче». При выполнении запроса на стороне клиента эти структуры доступа считываются постранично, точно так же, как если бы они являлись индексами коллекций ObjectStore, и поэтому специальные структуры доступа полностью эквивалентны индексам на коллекциях, поскольку и те, и другие структуры оптимизируют доступ к бизнес-объектам, и одним и тем же образом. Я не думаю, что подобные специальные индексы можно реализовать, если не опираться на архитектуру запросов на стороне клиента, или если не компоновать каким-то образом пользовательский код на C++ в процесс на стороне сервера, что, как показывает опыт, порождает проблемы безопасности и надежности сервера.
«Эффективность выполнения запросов может обеспечиваться путем использования общесистемных идентификаторов, многопотоковой, удаленной и распределенной обработки, агрегирования.»
В архитектуре, основанной на страницах, могут использоваться и на самом деле используются общесистемные идентификаторы. Точно так же в ООСУБД с архитектурой, основанной на страницах, возможны и параллельные и распределенные запросы, поскольку клиенты могут быть многопотоковыми, и они могут в одно и то же время использовать персистентные объекты более чем из одной базы данных. По причине поддержки отображения адресов клиентский указатель на персистентный объект эквивалентен общесистемному идентификатору; для интерпретации указателя не требуется контекст базы данных.
В ObjectStore также обеспечиваются общесистемные идентификаторы, называемые «мягкими указателями» («soft pointer»), которые уникально идентифицируют любой персистентный объект во всех базах данных ObjectStore, глобально, в любой области видимости и независимо от контекста любого существующего процесса. Мягкие указатели на уровне схемы совместимы с обычными указателями C++ в 32- и 64-разрядных архитектурах.Они также очень эффективны в том отношении, что после первого использования работают с той же скоростью, что и прямые указатели C++.
В архитектурах, основанных на страницах, может использоваться и реально используется многопотоковость как в реализации продукта, так и на уровне программистов. В ObjectStore пользователи могут писать многопотоковый код с использованием соответствующих библиотек поддержки безопасного многопотокового программирования.
В архитектурах, основанных на страницах, и, в частности, в ObjectStore можно напрямую поддерживать параллельные распределенные запросы. На самом деле, как описывалось выше, ObjectStore отличается тем, что позволяет разработчикам системы распределять обработку запросов по машинам клиентов, избегая узких мест на сервере, или выполнять запросы локально на сервере. При разработке ObjectStore эта возможность имелась в виду с самого начала.
Сетевая модель
У трех рассматриваемых архитектур имеются разные сетевые модели, влияющие на использование пропускной способности и производительность распределенной системы. Каждая сетевая модель тесно связана с моделью блокировок, поскольку после получения блокировки требуется пересылка информации, если только она уже не находится в локальном кэше. Низший уровень гранулированности передачи по сети связывается с низшим уровнем гранулированности блокировок.
Сетевая модель в архитектуре, основанной на контейнерах
Для этой архитектуры единицей пересылки при запросе объекта является контейнер. Все объекты должны располагаться в контейнере, и клиентский запрос объекта разрешается на сервере NFS, посылающем по сети страницы контейнера клиенту, в котором контейнер кэшируются, и обеспечивается последующий доступ к объекту. При пересылке контейнера пересылаются все содержащиеся в нем объекты, независимо от того, требуется ли к ним доступ. Блокировка устанавливается на весь контейнер, независимо от того, к каким объектам требуется доступ запросившей транзакции.
Сетевая модель в архитектуре, основанной на объектах
Для этой архитектуры единицей пересылки при запросе объекта являются объект или группа объектов в зависимости от заданной глубины связей, как проиллюстрировано ниже. Объект может являться коллекцией, и в этом случае при одном сетевом взаимодействии пересылается много объектов. В запросе объекта или коллекции может указываться глубина связей; тогда при одном сетевом взаимодействии вместе с указанным объектом будут пересылаться связанные с ним объекты в пределах указанной глубины связей. При пересылке объекта на него устанавливается блокировка. В случае коллекции или при указании глубины связей блокируются все объекты, посылаемые клиенту. Например, для загрузки в клиента всех объектов, показанных на приведенной ниже иллюстрации, потребовалось бы три сетевых запроса. Если запрашивались бы только служащие или какой-то отдельный служащий, понадобилось бы только одно сетевое взаимодействие. Возможны специальные запросы с указанием глубины связей, позволяющие устранить загрузку избыточных объектов с одновременной минимизацией числа сетевых взаимодействий.
Скорость эволюции схемы
Этот вопрос является вариантов классического вопроса «насколько длинна часть строки?». Это зависит от контекста, деталей эволюции схемы, скорости дисковой системы, размера базы данных, объема физической основной памяти и числа процессоров в машине, производящей эволюцию схемы.
Средство эволюции схем ObjectStore разработано и оптимизировано для наиболее эффективной работы в среде высокопроизводительных серверов, на которых обычно развертываются базы данных ObjectStore.
Имеются три основных фазы эволюции схемы:
На первой фазе выполняются предэволюционные преобразования, детали которых здесь неважны. На второй фазе выясняется местоположение всех кандидатов на эволюцию, и вычисляются их целевые местоположения; эта информация сохраняется в очень сжатой, но быстро работающей таблице отображения из старых адресов в новые адреса. Наконец, на последней фазе читаются все страницы базы данных, производятся изменения объектов, они перемещаются из старых местоположений в новые местоположения, исправляются значения всех указателей, участвующих в ссылках. Необходимо прочитать все страницы, поскольку было бы слишком дорого предварительно вычислять, в каких страницах содержатся указатели на кандидатов на эволюцию. Кроме того, поскольку порядок объектов в постоянной памяти не изменяется, могут перемещаться не только эволюционирующие объекты.
Однако при этом отсутствует троекратное чтение всех страниц базы данных. Первые две фазы могут выполняться без чтения и отображения страниц, содержащих реальные объекты. С каждой страницей в базе данных ObjectStore ассоциируются метаданные, которые задают местоположение и тип каждого объекта внутри этой страницы, а также указатель на начало страницы. Эта информация сильно сжата, и обычно ее размер гораздо меньше размера страницы. На первых двух фазах требуется прочитать только эту метаинформацию, и поэтому они выполняются на порядки величин быстрее, чем если бы требовалось считывать и отображать страницы целиком. Так что в терминах считывания страниц выполняются не три прохода по базе данных, а скорее 1.2 проходов.
Ответ состоит в том, что серверная архитектура, основанная на страницах, позволяет писать C++-клиентов таким образом, как если бы они работали со структурами данных в основной памяти, вместо того, чтобы заставлять программистов использовать API для навигации по базе данных. Для программиста, использующего ObjectStore, наилучшей метафорой является не «база данных», а «замороженная транзакционная куча» («frozen transactional heap»). Это снижает сложность приложений и повышает скорость приложений, выполняющих большую работу над подсетью объектов, которая может быть кэширована.
При эволюции схемы имеется в точности противоположный сценарий использования. В этом сценарии считываются все страницы базы данных, но над каждой страницей выполняется очень небольшой объем работ.
«Ключевое отличие в реализации систем с физической идентификационной информацией приводит к существенному отличию по части масштабируемости при управлении «сырыми» данными на дисках. Применение в архитектуре, основанной на страницах, подхода трансляции адресов делает невозможным истинно интегрированное распределение данных на диске. Можно сегментировать данные внутри одной базы данных, чтобы приложения работали с подобластями этой базы данных в адресных пространствах своих процессов, но невозможно обеспечить в дисковой подсистеме распределенную (федеративную) базу данных, доступную клиентам в виде логического источника данных. Это означает, что в реализации архитектуры, основанной на контейнерах, возможности масштабируемости распространяются до терабайтных и даже петабайтных диапазонов, но в реализации архитектуры, основанной на страницах, они ограничены диапазонами мегабайт или гигабайт.»
Это просто неверно. Кажется, Грин полагает, что вся база данных должна помещаться в адресное пространство клиента. Конечно, это не так. Виртуальное адресное пространство расходуется только для действительно затрагиваемых страниц (на самом деле, групп страниц общим размеров в 64 Кб) и для страниц, на которые ссылаются «жесткие» указатели, находящиеся в затрагиваемых страницах.Кроме того, виртуальное адресное пространство динамически переназначается ObjectStore для обеспечения приложению возможности доступа к более крупной части персистентного хранилища. Даже на 32-разрядных клиентских машинах нет ограничения на размер базы данных, кроме ограничений на размер файла, и нет ограничений на одновременное интегрированное использование нескольких баз данных. С появлением 64-разрядного виртуального адресного пространства действительно отсутствуют ограничения на трансляцию адресов.
Следствия модели параллельности
Различные модели параллельности могут влиять на производительность приложений в многопользовательских средах, если эти приложения производят много обновлений. Приложения, в которых имеется много конфликтующих обновлений, потенциально тормозятся всеми моделями параллельности, но на то они и модели параллельности. Но в архитектурах, основанных на контейнерах и страницах, потенциально тормозятся и приложения, в которых имеется много не обязательно конфликтующих обновлений, поскольку блокировки производятся таким образом, что могут заблокировать доступ к другим объектам, не используемым в транзакции, что приводит к ложным ожиданиям и синхронизационным тупикам.
Кроме потенциальных ловушек ложных ожиданий и тупиков, гранулированность объектов, вовлекаемых в транзакции приложений, может оказывать в одной архитектуре большее влияние, чем в другой. В архитектуре, основанной на страницах, из-за возвратных вызовов по поводу блокировок при работе сильно гранулированной обновляющей системы требуется намного больше сетевых взаимодействий.
Приложения, у которых имеются относительно фиксированные модели с редко конфликтующими обновлениями, хорошо сегментирующиеся в независимый кластер, обычно ведут себя наилучшим образом в архитектурах, основанных на контейнерах и страницах. Так получается, потому что требуется только высокоуровневая блокировка, а почти не конфликтующие обновления позволяют минимизировать работу по координации блокировок. Одним из примеров, хорошо соответствующих этим характеристикам, являются приложения САПР (системы автоматизированного проектирования). Для приложений этого типа использование ООСУБД может обеспечить повышение производительности на порядки величин по сравнению с традиционными реляционными базами данных. Приложения с более высоким уровнем конфликтов по обновлению и большей гранулированностью моделей обычно лучше всего себя ведут на архитектуре, основанной на объектах.
Уровень влияния параллельного доступа на производительность обычно связан с числом одновременно работающих пользователей или процессов.
Упомянутые выше проблемы усиливаются при увеличении числа пользователей или процессов, взаимодействующих с базой данных. В большинстве случаев архитектура, основанная на объектах, лучше всего подходит при большом числе одновременно работающих пользователей и/или процессов, особенно в системах, которые не могут быть хорошо сегментированы для изоляции.
К другим проблемам, окружающим модели параллельного доступа, относится долговременное будущее использование данных, включая эволюцию и сопровождение приложений. Во многих системах для конкретного приложения и его паттернов доступа к данным можно обеспечить фиксированные, хорошо сегментированные данные. Важный вопрос касается введения новых приложений или расширения возможностей существующих приложений над исходными данными. Для новых приложений и возможностей часто требуются другие паттерны доступа. По мере увеличения числа паттернов доступа становиться более трудно обеспечить фиксированные и хорошо сегментированные схемы, и основной мерой является изменение размещения объектов. В действительности, в процессе выбора архитектуры приверженцу объектной технологии следует тщательно проанализировать будущее использование данных.
Как обсуждается ниже, подход к координации блокировок оказывает огромное влияние на сетевую модель, поскольку при обновлении базы данных требуется сетевая активность для обновления кэшей клиентов.
Следствия модели запросов
Разные модели запросов могут существенно влиять на эффективность приложения. Основными факторами для достижения оптимальной производительности являются (1) место выполнения запросов, (2) гибкость при выборе того, что может запрашиваться, (3) возможности индексации.
Реализации ООСУБД всегда фокусируются на навигационном доступе, как основном способе выборки информации из базы данных. Здесь снова не ставится цель проанализировать все возможности механизма запросов, такие как проецирование, агрегация, представления, математические вычисления, курсоры, составные индексы и т.д., и т.п. Целью является обеспечение понимания основ реализации. Как правило, ни одна из реализаций не была настолько полной, как те, которые обеспечиваются в реляционном мире, где весь доступ к данным производится через запросы. К счастью, эта ситуация изменяется по мере того, как все поставщики приходят к новому пониманию взаимного дополнения ролей запросов и навигационного доступа. Можно достаточно уверенно сказать, что после почти двух десятилетий существования ООСУБД во всех реализациях имеются зрелые возможности, делающие их полезными, и реальные вопросы теперь затрагивают масштабируемость и производительность.
Эффективность операций запросов более всего страдает в тех ситуациях, когда объекты, потенциально удовлетворяющие запросу, требуется перемещать в адресное пространство другого запроса для оценки предиката. Эта проблема свойственна реализациям механизма запросов в архитектурах, основанных на страницах и контейнерах. В особенности в этом отношении уязвима архитектура, основанная на страницах, поскольку запросы могут адресоваться только к объектам, входящим в специальные коллекции, и индексы применимы только к этим коллекциям. Для выполнения запроса по сети должны загружаться все страницы, содержащие объекты в этих коллекциях или индексы для этих коллекций. Эти страницы, безусловно, будут содержать много лишних объектов, что приводит к неэффективному расходованию пропускной способности сети и падению производительности, поскольку на пересылку страниц может тратиться больше времени, чем на реальную обработку запроса.
Можно представить себе ситуацию, в которой имеется миллион объектов Foo, среди которых ищется всего один объект. Вдобавок к этому при каждой вставке требуется поддержка индексов, а поскольку потенциально они могут быть распределены между многими клиентскими процессами, может возникнуть много недействительных страниц, и понадобятся сетевые обновления коллекций и индексов, дополнительно нагружающие сеть.
Архитектура, основанная на контейнерах, ведет себя таким же образом, что и архитектура, основанная на страницах. Однако запросы могут обрабатываться в удаленном режиме, могут расщепляться, и индексы поддерживаются в централизованном процессе. Поэтому, хотя контейнеры объектов и/или индексов должны загружаться для обработки в другой процесс, выполнение запроса может производиться в параллель, и по сети нужно пересылать в исходное звено обратившегося процесса только контейнеры, удовлетворяющие предикату. Хотя это решение не идеально, оно является более масштабируемым, чем механизм запросов в реализации архитектуры, основанной на страницах.
Реализация архитектуры, основанной на объектах, в которой, подобно РСУБД, индексирование и выполнение запросов происходит на стороне сервера, обеспечивает механизм запросов, наиболее близкий к оптимальному. Отсутствует потребность в пересылке между процессами каких-либо избыточных объектов, индексы управляются локально, и оптимизация запросов производится в процессе сервера баз данных, в результате чего возвращается только объект или набор объектов, удовлетворяющих предикату. Кроме того, многопотоковая организация клиентского процесса позволят выполнять запросы к нескольким физическим базам данных в параллель.
При использовании систем с архитектурой, основанной на страницах или контейнерах, управление индексами обычно интегрируется с кодом приложений. Это влияет на сопровождение приложений. При использовании систем с архитектурой, основанной на объектах, индексы управляются сервером. Новые индексы могут определяться без изменения приложений.
В этой выдержке содержится много неточностей и полуправды. Во-первых, серверные архитектуры не являются автоматически уязвимыми к требованию перемещения страниц в адресные пространства других процессов при обработке запросов. Конкретный продукт можно реализовать таким образом, что он будет уязвимым к этому требованию, но в серверных архитектурах, основанных на страницах, как таковых можно реализовать обработку запросов на стороне сервера, если такой подход является желательным. Перемещать или не перемещать страницы в адресные пространства других процессов – это проблема, зависящая от многих аспектов, среди которых не последними являются сценарий использования и эффективность реализации продукта.
Как подробно описывалось выше, в случае ObjectStore при наличии соответствующих индексов при обработке запроса требуется перемещение на клиента только страниц, содержащих индексные структуры. Не буду развивать здесь тему о смягчении проблемы узких мест сервера.
Во-вторых, в серверных архитектурах, основанных на страницах, в принципе не навязывается то ограничение, что можно запрашивать только объекты, существующие в особых коллекциях, для которых могут иметься или не иметься индексы. В конкретных продуктах может реализовываться библиотека коллекций, но эта особенность ортогональна к специфике серверной архитектуры, основанной на страницах.
В ObjectStore запросы производятся путем вызова метода запросов на его классах коллекций, и он возвращает поднабор элементов коллекции на основе сложного предиката; эти запросы могут индексироваться и эффективно обрабатываются. В ObjectStore также допускается доступ к произвольным объектам двумя способами: с использованием объектного курсора и на основе динамических экстентов. Так что в ObjectStore достижим любой объект без родителей. Причина того, что произвольные запросы к глобальным объектам не рекомендуются в качестве основного режима доступа, кроется в проблемах инкапсуляции.
Так что замечание Грина о том, что «запросы могут адресоваться только к объектам, входящим в специальные коллекции», в действительности является довольно бессмысленным; аналогично можно было жаловаться на то, что в реляционных базах данных запросы могут адресоваться только к таблицам.
Кроме того, это в любом случае неверно, поскольку в ObjectStore поддерживаются произвольные запросы через динамические экстенты.
Интересным побочным аспектом является смысл объектов, не имеющих родителей. Объекты-сироты, присутствующие в базе данных ObjectStore, – это объекты, на которые отсутствуют ссылки из других объектов. Это значит, что никакая программа не может получить доступ к этим объектам, кроме как путем использования объектного курсора. Если считать, что используется навигационный доступ, как это и рекомендуется, то наличие этих осиротелых объектов утечки персистентной памяти (memory leak); клиентский код не может удалить более не нужный объект.
«Можно представить себе ситуацию, в которой имеется миллион объектов Foo, среди которых ищется всего один объект. Вдобавок к этому при каждой вставке требуется поддержка индексов, а поскольку потенциально они могут быть распределены между многими клиентскими процессами, может возникнуть много недействительных страниц, и понадобятся сетевые обновления коллекций и индексов, дополнительно нагружающие сеть.»
В правильно спроектированной системе, основанной на ObjectStore, нахождение единственного объекта Foo в коллекции из миллиона объектов обычно является очень быстрым вызовом. Для этого не требуется какая-либо работа программистов!
Здесь Грин рисует очень неточную картину поддержки индексов в ObjectStore. При вставке объекта в индексируемую коллекцию инициируется автоматическая поддержка индекса. В представленном сценарии, в котором одиночный объект Foo вставляется в коллекцию из миллиона объектов, будет обновляться одна страница индекса, или, может быть, несколько страниц. При фиксации транзакции ObjectStore (в предположении, что фиксация выполняется) страницы, содержащие индексные структуры данных, возвращаются на сервер и записываются на диск, как и любые другие страницы. Если к индексу обратится другой клиент, эти несколько страниц будут выдаваться обычным образом страница за страницей. Измененные индексные страницы пересылаются только в тех случаях, когда они используются другими клиентами, и никакие другие страницы, например, те, которые содержат миллион объектов Foo, не пересылаются в результате обновления этого индекса.
« Кроме того, многопотоковая организация клиентского процесса позволят выполнять запросы к нескольким физическим базам данных в параллель.»
Здесь Грин в действительности не различает серверные архитектуры, основанные на объектах и страницах. Из контекста этого предложения следует, что эта черта возможна только при выполнении запросов на стороне сервера и, следовательно, только в архитектуре, основанной на объектах. Истина здесь состоит в том, что серверные архитектуры, основанные на страницах, сами по себе не навязывают какую-нибудь единую модель многопотоковости и не препятствуют параллельному выполнению запросов над несколькими физическими базами данных. Это доказывается существованием реализации серверной архитектуры, обеспечивающей эти возможности. В ObjectStore, типичной реализации серверной архитектуры, основанной на страницах, активно поддерживается и поощряется многопотоковое программирование на стороне клиента, и допускается транзакционный доступ клиентов к нескольким физическим базам данных, распределенным по сети и расположенным на разных платформах под управлением разных серверных процессов ObjectStore. В том, что говорит здесь Грин, не дифференцируются архитектуры, основанные на страницах и объектах, и даже не дифференцируются продукты на каком-либо содержательном уровне.
«При использовании систем с архитектурой, основанной на страницах …, управление индексами обычно интегрируется с кодом приложений. Это влияет на сопровождение приложений. При использовании систем с архитектурой, основанной на объектах, индексы управляются сервером. Новые индексы могут определяться без изменения приложений.»
Тот факт, что управление индексами интегрируется или не интегрируется с кодом приложений, не связан с тем, что система имеет серверную архитектуру, основанную на страницах. В ObjectStore имеется несколько способов поддержки индексов, для некоторых из которых, как отмечает Грин, требуются незначительные изменения кода приложений. Но этот код не является сложным, и в любом случае его написание не представляет собой проблему, это всего лишь код, выполняющий некоторую полезную работу.
С точки зрения сопровождения кода индексы ObjectStore также обычно не порождают проблем. Во многих ситуациях необходима явная поддержка индексов, которая производится автоматически во время выполнения ObjectStore.
Неверно также и то, что в ObjectStore нельзя определять индексы без изменения приложения, поскольку эта функциональная возможность может быть обеспечена путем написания дополнительного специального приложения поддержки индексов. API для добавления индексов является очень простым и ориентированным на программистов C++. Определить новый индекс в ObjectStore не сложнее, чем написать операторы DDL для добавления индексов над реляционными таблицами. Поэтому относительно просто написать автономное приложение для добавления или уничтожения индексов над любой коллекцией базы данных, даже если система используется другими приложениями, и эти приложения смогут начать использовать заново созданные индексы сразу после фиксации транзакции, в рамках которой они были созданы. Так что индексы в ObjectStore можно создавать и удалять динамически, «на лету» без изменения кода основных приложений.
Следствия сетевой модели
Различные сетевые модели могут влиять на производительность приложений за счет воздействия на число сетевых взаимодействий и коэффициент полезного использования пропускной способности сети, на который влияет объем значимых данных, пересылаемых по сети. Поведение в сетевой среде тесно связано с моделями приложений и характеристиками доступа.
Приложение с почти фиксированной моделью доступа, хорошо сегментируемой в независимый кластер с не конфликтующими обновлениями и/или не слишком большим числом транзакционных обновлений, как правило, хорошо выполняется в архитектуре, основанной на контейнерах или страницах. Это связано с ограниченным числом сетевых взаимодействий, поскольку фиксированный контейнер связанных объектов захватывается и перемещается в кэш клиента за одно сетевое взаимодействие. Фиксированные модели, относящиеся к сценарию, в котором доступ к логически связанным объектам производится одновременно в одной транзакции, хорошо работают в этих архитектурах, поскольку все логически связанные объекты попадут в клиентский кэш сразу после первого доступа к объекту, и сетевые взаимодействия минимизируются с высоким коэффициентом полезного использования пропускной способности сети. Архитектура, основанная на объектах, обычно приводит к большему числу сетевых взаимодействий, по одному обмену по сети на каждый уровень глубины связей в модели. Например, для модели на приведенном выше рисунке могло бы потребоваться в три раза большее число сетевых взаимодействий. Однако так произойдет, если все объекты, показанные на этом рисунке, находятся в одном и том же контейнере или в одной и той же странице. Если они сегментированы (физически кластеризованы) по-другому, то в архитектуре, основанной на контейнерах или страницах, может потребоваться даже большее число сетевых взаимодействий, чем в архитектуре, основанной на объектах. По этой причине для архитектур, основанных на контейнерах и страницах, сетевая производительность, а фактически, и производительность в условиях параллельного доступа существенно связана с физическим размещением объектов на диске.
В приложении с такими характеристиками коэффициент полезного использования пропускной способности будет достаточно высоким во всех архитектурах, поскольку будут пересылаться группы логически связанных объектов, и избыточные объекты пересылаться по сети не будут.
При возрастании числа конфликтующих обновлений производительность систем с архитектурами, основанными на контейнерах и страницах, уменьшается, и преимущество получают архитектуры, основанные на объектах. Это связано с тем, что конфликтующие обновления вызывают необходимость обновления кэшей конфликтующих клиентов. Для архитектур, основанных на контейнерах и объектах, это означает, что для поддержания согласованности кэшей по сети требуется пересылать страницы или контейнеры. Наибольшей потерей здесь является пропускная способность сети, поскольку при обновлении единственного объекта из нескольких байт потребуется обновить в кэше потенциально большого числа клиентов несколько тысяч байт – страницу или контейнер. Требуется полное обновление страницы или контейнера, поскольку в соответствующей сетевой модели они являются наименьшими единицами гранулированности.
Поясним, что здесь имеется в виду под конфликтующими обновлениями. Это не обязательно означает обновление одного и того же объекта. Конфликтуют обновления объектов, которые находятся в одной и той же странице или одном и том же контейнере. Поэтому в архитектурах, основанных на контейнерах и страницах, требуется хорошая сегментация модели в физическом кластере, поскольку, если модель не является хорошо сегментированной, при обновлении даже не связанных объектов может возникнуть конфликт на уровне страницы или контейнера, который приведет к ожиданию блокировке и нагрузке на сеть для обеспечения согласованности кэшей. Причина, по которой в архитектурах, основанных на контейнерах и страницах, требуется избегать небольших транзакционных обновлений, кроется в том, что при обновлении требуется произвести запись по сети в долговременное хранилище наименьшей единицы передачи данных – страницы или контейнера.
Если обновления хорошо группируются в одну транзакцию, затрагивающую единственный контейнер или страницу, то производительность в таких архитектурах не падает, и в действительности производительность приложения будет намного выше, чем при использовании традиционной реляционной системы.
При возрастании числа конфликтующих обновлений сетевая модель, основанная на объектах, демонстрирует преимущества, поскольку гранулированность пересылок гораздо меньше. Если обновляется объект размером всего в несколько байт, и требуется обновление кэшей клиентов для поддержания их согласованности, то по сети передается всего несколько байтов. Кроме того, немного изменяется понятие конфликтующих обновлений. Во всех архитектурах обновления вынуждают работать механизм согласования кэшей. Поэтому, если модели являются фиксированными и хорошо сегментированными, то архитектуры, основанные на объектах, страницах и контейнерах, ведут себя почти одинаково, если не считать того, что различается объем данных, пересылаемых по сети. Однако если модели не являются хорошо сегментированными, и/или связи между объектами соединяют страницы или контейнеры, которые содержат всего несколько объектов, участвующих в связи, то потенциально потребуется обновлять в кэше многие страницы или контейнеры, содержащие многочисленные избыточные объекты. Это означает, что пропускная способность сети будет использоваться для пересылки избыточных объектов наряду с теми, которые действительно должны быть обновлены в кэше.
Еще одним следствием механизма параллельного доступа и сетевой модели для каждой архитектуры является то, как они ведут себя в трехзвенной архитектуре, будучи встроенными в промежуточное программное обеспечение сервера приложений. Здесь применимы все отмеченные выше аспекты, поскольку кэширование производится локально на серверах приложений, и поэтому для поддержания согласованности кэшей в кластере серверов приложений, который, по существу, является кластером клиентов ООСУБД, требуется обсуждавшееся ранее поведение.
Конечно, на все упомянутое выше влияет увеличение числа пользователей или процессов, получающих доступ к базе данных. Рассмотренные проблемы усложняются при возрастании числа пользователей или процессов. Вообще говоря, архитектура, основанная на объектах, лучше всего подходит для обслуживания большого количества пользователей или процессов, особенно в системах, которые не являются хорошо сегментированными для изоляции, или в которых требуется гибкость для обеспечения в будущем новых паттернов доступа.
Надеюсь, что из приведенного обсуждения стало ясно, что у сетевой модели каждой архитектуры имеются свои преимущества и недостатки. В архитектурах, основанных на страницах и контейнерах, для достижения оптимальной производительности требуется тщательное физическое планирование моделей приложений. В правильно сегментированной фиксированной модели это способствует эффективному использованию пропускной возможности сети. В архитектуре, основанной на объектах, для достижения оптимальной производительности требуется понимание логических сценариев использования. В отличие от архитектур, основанных на страницах и контейнерах, в которых требуется понимание физического моделирования, в архитектуре, основанной на объектах, требуется понимание логики, так что в каждом приложении могут использоваться специальное кодирование или метаданные для определения загрузки модели на основе сценариев использования. Эта работа способствует достижению высокого уровня параллелизма и масштабируемости для исключительного большого числа транзакций, одновременно повышая уровень гибкости для обеспечения возможности перекрывающихся паттернов доступа при будущем использовании данных.
Сложность эволюции схемы
С инженерных позиций этот процесс является чрезвычайно сложным, поскольку, в отличие от реляционной базы данных, в которой персистентная схема является известной и постоянной – все данные располагаются в таблицах, столбцах и строках,– персистентная ООБД является полностью произвольной, определяемой в реальном пользовательском коде C++. Поэтому у любой утилиты общего назначения, поддерживающей эволюцию схемы, должная иметься возможность управления буквально любой объектной моделью C++, которая может встретиться, и возможность преобразования ее в любую другую модель. Для снижения уровня сложности и по соображениям эффективности в ObjectStore утилита поддержки эволюции схем функционирует в режиме оф-лайн, а для уменьшения сложности возможных изменений схемы среди программистов распространен прием написания специального кода эволюции схемы на C++ и его компоновки с библиотекой эволюции схемы ObjectStore для выполнения процесса эволюции схемы.
Так что с позиций пользователя эволюция схемы также представляет некоторую сложность. Рассмотрим пару примеров.
Управление идентификационной информацией
Во всех основных архитектурах различаются подходы к реализации управления идентификационной информацией, хотя принципиально эти подходы различаются одним аспектом. По своей природе идентификационная информация может быть физической или логической. В ООСУБД идентификационная информация используется для обеспечения уникальности и реализации связей. Упомянутое различие в реализации имеет глубокие последствия для долговременного функционирования и гибкости. Кроме того, детали реализации физической идентификационной информацией влияют на масштабируемость данных для систем, требующих хранения терабайт информации.
В защиту статьи «Архитектуры ООСУБД»
Роберт Грин, вице-президент компании Versant
Перевод: Сергей Кузнецов
Оригинал: OODBMS Architectures Defended
Эта статья является продолжением дискуссии об архитектурах ООСУБД
В этой статье я собираюсь обсудить проблемы, поднятые в статье Адриана Мариотта «Еще раз об архитектурах ООСУБД». В качестве вступления замечу, что в своей исходной статье «Архитектуры ООСУБД» я стремился предоставить сторонникам технологии ООБД некоторые практические советы, облегчающие ее успешное внедрение. Ставилась задача привести именно практические, а не теоретические рекомендации, поскольку, в конечном счете, каждому из нас приходится создавать масштабируемые системы при наличии практических ограничений индивидуальных компонентов этих систем.
И поэтому я думаю, что необходимо высказаться по поводу возражений Мариотта, в которых, по моему мнению, много теоретических рассуждений, менее полезных для практических реализаций. Кроме того, мне хочется прояснить комментарии Мариотта, которые являются некорректными или приведенными в отрыве от контекста, выделяя при этом области, относящиеся к которым комментарии Мариотта являются законными.
Я буду стараться высказываться кратко и по существу, поскольку уже описал свои представления о данном предмете в исходной статье. Я не буду стремиться вдаваться здесь в описание конкретных возможностей ООСУБД Versant, поскольку эта информация должным образом приведена в соответствующих руководствах пользователя.
Когда началось коммерческое использование ООСУБД,
Когда началось коммерческое использование ООСУБД, реляционная технология уже была хорошо укоренившейся, и, хотя о ней все еще продолжали спорить, все основные поставщики РСУБД заняли свое место на рынке баз данных. В то время компании переходили из стадии принятия решений относительно инфраструктурной технологии баз данных в стадию принятия решений о применении этой технологии для повышения эффективности бизнеса. Были произведены и опубликованы почти все сравнения СУБД на основе эталонных тестовых наборов, таких как TPC. Комплекты приложений с желательными возможностями повышения эффективности бизнеса либо поступали от выбранного поставщика реляционных баз данных, либо разрабатывались с целью поддержки любой реляционной базы данных на основе стандартов SQL. Характеристики баз данных были достаточно близкими, так что трудно было рассчитывать на особые преимущества при выборе какого-нибудь одного поставщика. Поэтому решение об использовании какой-то определенной базы данных часто являлось скорее политическим, а не техническим, способствуя укреплению бизнес-связей, но не обязательно означая выбор «наилучшего» решения получателем приложения. Все действительно понимали, что между РСУБД разных поставщиков имеются лишь незначительные различия, и показатели производительности и масштабируемости отличаются на проценты, а не на порядки величин.
Вот здесь-то и сочетаются архитектура ООСУБД и ожидания пользователей. В то время как архитектуры РСУБД очень похожи (ориентация на клиент-серверную организацию, опора на индексы, процессор выполнения выражений реляционной алгебры) и обладают характеристиками производительности и масштабируемости, отличающимися на небольшие проценты, архитектуры ООСУБД значительно различаются и демонстрируют совершенно разные характеристики. После столь многих лет, в течение которых людей приучали, что все РСУБД ведут себя практически одинаково, было довольно естественно применить эти выводы к ООСУБД и объявить, что и они являются почти одинаковыми.
При использовании этого предположения, если какой-либо первопроходец выбирал некоторую ООСУБД, которая плохо соответствовала потребностям его приложений, можно было легко придти к заключению, что никакая ООСУБД не сможет удовлетворить эти потребности. Эти нелогичные рассуждения привели к возникновению неправильных представлений об ООСУБД: они являются слишком медленными, не обеспечивают высокий уровень параллелизма, не масштабируются для работы с большими объемами данных и т.д., и т.п. Все это неправильно, а реальность состоит в том, что требуется внимательно проанализировать характеристики приложения и понять, какая архитектура ООСУБД им более всего соответствует. При выборе правильной архитектуры СУБД показатели производительности и масштабируемости могут повышаться на порядки величин, а не на проценты, как в случае реляционных реализаций. Разобравшись в этом, проанализируем различия в архитектуре ООСУБД, чтобы помочь пользователям принимать решения, ведущие к успешному выбору технологии.
Понимание основ архитектурной реализации ООСУБД
Понимание основ архитектурной реализации ООСУБД и их сопоставление с характеристиками требуемого приложения может помощь успешному применению технологии объектно-ориентированных баз данных. В отличие от реляционной технологии, разные архитектуры ООСУБД в зависимости от характеристик приложения могут обеспечивать производительность и масштабируемость, различающиеся на порядки величин. Архитектуры, основанные на страницах и контейнерах, обеспечивают хорошее решение при наличии относительно фиксированных моделей, низкого или среднего уровня параллельного доступа и правильно сегментированных данных. В архитектуре, основанной на объектах, понятия блокировки, размещения и перемещения объектов являются независимыми, и эта архитектура обеспечивает наилучшее решение при наличии высокого уровня параллелизма, возрастании масштабности и изменчивости приложений и данных.
При выборе архитектуры ООСУБД следует тщательно проанализировать такие аспекты, как возрастание объема данных со временем, число параллельно работающих с базой данных пользователей или процессов, ожидаемые в будущем требования к изменению моделей предметной области, уровень гранулированности моделей и ожидаемые требования к сети. Снова и снова оказывается, что объектная база данных может быть более предпочтительным решением по сравнению с реляционной технологией при наличии сложных моделей предметной области, не укладывающихся должным образом в парадигму реляционного хранения данных. Выбор ООСУБД с архитектурой, соответствующей характеристикам приложения, является ключом для доступа ко всем возможностям технологии и достижения успеха.
Думаю, что важно подчеркнуть практическую природу моей исходной статьи и повторить, что имеются разные архитектуры ООСУБД, и это влияет на производительность и масштабируемость приложений в зависимости от их характеристик. Следует надеяться, что эти дополнительные тексты, написанные мной и Мариоттом, позволят читателям лучше узнать принципы работы ООСУБД и научиться выбирать ту из них, которая может оказаться более полезной. ООСУБД являются мощной альтернативой традиционной технологии баз данных и могут служить хорошей основой для разработки приложений.