Публикации
Эволюционное проектирование баз данных

Перевод статьи Evolutionary Database Design
Мартин Фаулер
Прамод Саладаж

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


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

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

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

Работа с изменениями

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

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

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

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

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

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

В ходе последних трех лет мы были вовлечены в крупный проект (под названием Atlas), в котором было использовано эволюционное проектирование баз данных. В проекте участвовало почти 100 человек на нескольких площадках по всему миру (в США, Австралии, Индии). Объем разработки — примерно полмиллиона строк кода и более 200 таблиц. База данных разрабатывалась на протяжении полутора лет, в настоящее время она эксплуатируется несколькими заказчиками и продолжает развиваться. В этом проекте мы начали с итераций длительностью в месяц, но потом перешли к более эффективным двухнедельным итерациям.

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

Ограничения

Прежде чем начать описание приемов, важно отметить, что мы не решили все проблемы эволюционного проектирования баз данных. В частности:

  • Мы разрабатывали базу данных для отдельного приложения, а не интеграционную, объединяющую несколько баз.
  • Условия эксплуатации базы не требовали режима постоянной готовности (24/7).

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

Практики

Наш подход к эволюционному проектированию баз данных основывается на ряде важных практик.

Администратор базы данных тесно сотрудничает с разработчиками

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

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

Чтобы это работало, нужно, чтобы администратор базы данных был легкодоступен. Позвольте разработчику подойти на пару минут и задать несколько вопросов. Нужно, чтобы разработчики и администратор сидели недалеко друг от друга и могли легко контактировать.

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

Каждому - по экземпляру базы данных

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

Проектирование базы данных может вестись так же. Поэтому важно, чтобы у каждого разработчика была своя «песочница», где он может экспериментировать, ни с кем не пересекаясь. Многие опытные администраторы баз данных считают множественные экземпляры базы данных проклятием, полагая, что ими слишком трудно управлять. Однако, по нашему опыту, можно легко справиться с сотней или около того баз данных. Главное — иметь инструменты, позволяющие работать с базами так же, как вы работаете с файлами.

Регулярная интеграция с общей мастер-базой

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

Возьмем для примера Майка, который начинает работать над задачей в 10 утра. В рамках задачи ему нужно внести изменения в схему базы данных. Если изменение простое, например добавление колонки, он самостоятельно решает, как его выполнить. Кроме того, Майк проверяет, что такой колонки еще нет, на основе словаря данных (см. ниже). В более сложных случаях он обращается к администратору базы данных и обсуждает изменения с ним.

Как только он готов приступить к реализации задачи, он копирует к себе мастер-базу и спокойно модифицирует схему и код. Поскольку он делает это в своей  «песочнице», выполненные изменения больше никого не затрагивают. Через некоторое время, скажем, в 3 часа дня, даже если в части программирования готово еще не все, он вполне определяется с необходимыми изменениями базы данных. На этом этапе он снова отлавливает администратора, описывая ему изменения. Администратор при этом может поднять все вопросы, которые прошли мимо внимания Майка. В большинстве случаев проблем не оказывается, и администратор базы  отправляется выполнять изменения, осуществляя один или несколько рефакторингов базы данных (подробнее см. ниже). Майк может продолжить работу над задачей и в любое время фиксировать свой код в системе управления версиями, коль скоро администратор внес его изменения в мастер-базу.

Вы можете увидеть в этом принципе схожие черты с практикой непрерывной интеграции (практика описана в статье Continuous Integration), которая применяется для управления исходным кодом. В результате мастер-база существует под системой управления конфигурацией так же, как и исходные коды. Когда у нас образуется успешная сборка, база фиксируется в системе управления конфигурацией вместе с исходными кодами приложения, и мы имеем полную, синхронизированную историю версий того и другого.

Основную работу по интеграции исходных кодов берет на себя система контроля версий. Интеграция базы данных требует дополнительные усилия. Все изменения базы должны выполняться определенным образом — как автоматизированный рефакторинг (об этом чуть позже). Кроме того, администратор должен просматривать изменения базы данных и проверять их соответствие общей структуре базы. Чтобы этот подход работал четко, крупные изменения базы данных не должны становиться сюрпризом в момент интеграции — отсюда и необходимость тесного взаимодействия администратора с разработчиками.

Подчеркнем, что интеграция должна выполняться регулярно. По нашему опыту, значительно проще часто вносить мелкие изменения, чем изредка — крупные. Впечатление такое, что сложность интеграции с увеличением объема изменений  изменений растет экспоненциально. Раз так, внести много мелких изменений на практике легче, хотя многим интуиция подсказывает обратное. Аналогичный эффект для исходных кодов был отмечен участниками сообщества Software Configuration Management.

База состоит из схемы и тестовых данных

Говоря здесь о базе данных, мы имеем в виду не только схему, но и значительное количество данных. Последние состоят из постоянных данных приложения (таких, как список штатов США) и тестовых данных (например, нескольких тестовых записей о заказчиках).

Данные должны присутствовать в базе по ряду причин. Главная из них — обеспечение возможности тестирования. Мы твердо убеждены в целесообразности использования большой массы автоматизированных тестов для стабилизации разработки приложения. Большое количество тестов — обычный подход в гибких методологиях. Чтобы эти тесты могли эффективно работать, следует использовать базу с определенным набором данных, наличие которых перед запуском тестов обязательно.

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

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

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

Все изменения как рефакторинг базы данных

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

  • Изменение схемы
  • Миграция данных
  • Изменение кода доступа к базе данных

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

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

Многие рефакторинги базы данных, такие как добавление колонки, могут быть выполнены без обновления программного кода, обращающегося к базе. Если код использует новую схему, не зная, что добавлена колонка — последняя просто останется неиспользованной. Многие изменения, однако, не обладают таким достоинством. Мы называем их деструктивными, например запрет NULL в колонке, прежде допускавшей NULL-значения.

Деструктивные изменения требуют большей осторожности, степень которой зависит от величины возможных разрушений. Пример небольшого разрушительного эффекта — то же самое ограничение NOT NULL на колонке, допускавшей NULL. Возможно, в этом случае вы просто возьмете и выполните изменение. Процедура рефакторинга учтет все данные, имеющие NULL-значения. Обычно единственный разработчик, которого касается это поле, — тот же, кто инициировал изменение. Он же и обновит код взаимодействия с базой данных. В результате обновление не затронет других разработчиков. Если же все-таки случится обратное, то несоответствие обнаружится сразу после выполнения сборки и запуска тестов. (В нашем большом проекте мы оставляли себе некоторый простор для маневра, откладывая внесение изменений в базу данных на неделю).

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

Здесь важно подобрать для каждого типа изменений подходящую процедуру. Если вы колеблетесь между вариантами изменений, отдайте предпочтение более простому. В нашей практике самое простое решение оказывалось удачным чаще, чем можно ожидать, а если даже случится худшее — то при использовании строгой системы управления конфигурацией легко будет выполнить откат.

Автоматизируйте рефакторинг

Для некоторых языков программирования имеются средства, автоматизирующие ряд известных методов рефакторинга. Такая же автоматизация очень важна для баз данных в случае изменений схемы и миграции данных. Чтобы сделать автоматизацию возможной, каждое изменение базы данных оформляется в виде скрипта SQL DDL (для изменения схемы) или DML (для миграции данных). Эти изменения никогда не выполняются вручную, но напротив, всегда применяются к мастер-базе запуском скрипта.

Будучи выполненными, эти скрипты сохраняются, образуя журнал всех изменений, произошедших с базой данных в ходе рефакторинга. Впоследствии мы можем обновить любую копию базы данных до последней версии мастер-базы, накатывая скрипты изменений, сохраненные с момента создания экземпляра копированием мастер-базы.

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

Мы не вносили изменения в базы данных, находящиеся в эксплуатации, в ходе обычных итерационных циклов. Для выпуска новой версии, которая обычно происходит в конце одной из итерации, мы применяем к предыдущей версии полный набор изменений, зафиксированных с момента последнего выпуска. Это масштабное изменение и до сих пор мы выполняли его, только остановив работу приложения. (У нас есть соображения, как делать это в условиях постоянной готовности 24/7, но мы еще не испытывали их на практике). Разумно будет протестировать сценарий миграции прежде, чем выполнять его на живой базе. До сих пор этот подход работал у нас без нареканий. Разбив все изменения базы данных на множество простых, мелких изменений, мы реализовывали крупные изменения в эксплуатирующихся базах данных без осложнений.

Наряду с автоматизацией прямых изменений вы можете подумать об автоматизации отката каждого рефакторинга. Мы пока не занимались этим, поскольку не было острой необходимости, но принцип здесь тот же.

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

Автоматическое обновление персональных баз разработчиков

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

Мы автоматически обновляем каждую персональную базу каждый раз, когда меняется мастер-база. Тот же скрипт рефакторинга, который обновляет мастер-базу, обновляет и персональные экземпляры. Когда мы говорим о таком подходе, у некоторых появляются сомнения, не будет ли проблем при обновлении персональных баз без ведома разработчиков? Но по нашему опыту эта схема отлично работает.

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

Четко выделяйте код доступа базы данных

Для понимания последствий каждого рефакторинга важно видеть, как именно приложение использует базу. Если фрагменты SQL рассредоточены по всему коду, разобраться с этим будет очень трудно. Поэтому важно иметь выделенный слой доступа к базе. Мы советуем применять один из архитектурных паттернов для источников данных, описанных в книге «Архитектура корпоративных программных приложений».

Наличие выделенного слоя доступа к базе данных дает ряд дополнительных преимуществ. Минимизируется число участков системы, где разработчику необходимо знание SQL. Это позитивный момент, поскольку многие разработчики недостаточно сильны в SQL. Администратор базы, в свою очередь, видит ограниченный участок кода, в котором сосредоточена работа с базой. Это помогает при создании индексов, оптимизации базы данных, а также помогает определить, как можно изменить SQL для повышения производительности. Все это позволяет администратору лучше понять взаимодействие приложения с базой.

Вариации

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

Множественные линии базы данных

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

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

Вам не нужен администратор базы данных

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

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

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

Полезные инструменты

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

Одно из главных средств автоматизации — простой набор скриптов для выполнения типовых действий с базами:

  • Обновить пользовательскую базу до состояния мастер-базы.
  • Создать нового пользователя базы данных.
  • Скопировать схему базы данных (например, Сью обнаруживает баг в своей базе, тогда Майк может скопировать ее базу к себе и попробовать отладить приложение).
  • Переместить базу, например с одной рабочей станции на другую (по сути, это комбинация копирования с последующим удалением).
  • Удалить пользователя.
  • Экспортировать пользовательскую базу (этим способом участники команды могут делать автономные резервные копии базы, с которой работают).
  • Импортировать пользовательскую базу. Имея резервную копию, разработчик может импортировать копию и создать новую схему базы данных.
  • Сделать резервную копию мастер-базы. Это особый случай экспорта пользовательской базы.
  • Сформировать отчет о различиях между схемами (благодаря этой функции Майк может видеть, чем его база отличается от базы Сью).
  • Сравнить схему с мастером (благодаря чему разработчики могут увидеть отличия локальной копии от мастер-базы).
  • Получить список пользователей.

Часто аналитикам и отделу качества нужно иметь возможность видеть тестовые данные в базе и легко их менять. Для этих целей мы создали Excel-приложение с VBA-скриптами, которое позволяет выгрузить данные из базы в файл Excel, отредактировать их и загрузить обратно в базу. Хотя для редактирования и просмотра данных в базах есть специальные инструменты, Excel хорош тем, что большинство людей хорошо умеют работать с ним.

Каждый участник проекта должен иметь возможность разобраться в проекте базы данных: какие есть таблицы, как они используются. Для этого мы создали HTML-интерфейс, использующий сервлеты для запроса метаданных базы. Теперь, перед тем как добавить колонку, Майк может проверить — вдруг она уже есть — при помощи поиска по метаданным таблиц и колонок. Мы применяли для моделирования ErWin и получали данные из него в наши собственные таблицы метаданных.

Дальнейшие шаги и дополнительная информация

Конечно, эта статья не ставит точку в разговоре об эволюционном проектировании баз данных. Определенно хотелось бы понять, как расширить эти приемы для интеграционных баз данных, для режима постоянной готовности 24/7 и других, не изученных нами проблемных областей. Если вы хотите узнать больше по этой теме, или поделиться своим опытом, то можете воспользоваться ресурсом yahoo egroup for agile databases.

Перевод - С.Мищук, заместитель директора производственного отделения ДИР

Главная страница Карта сайта Напишите нам www.ibs.ru Главная страница Карта сайта Напишите нам www.ibs.ru Департамент интеграционных решений