Отслеживание изменений базы данных вашего приложения является не легкой задачей. Как правило, схемы баз данных не совпадают в различных средах, данные в одной БД могут не иметь некоторых важных частей данных. Такие обстоятельства могут быть неприятными, особенно если возникают в production.
Ситуация становится еще хуже, если вы разработчик распространяемого ПО. В этом случае, каждый ваш клиент имеет собственный экземпляр БД, структура которого может отличаться от других. В таких проектах, отслеживание изменений БД клиентов может стать кошмаром.
Давайте рассмотрим лучшие подходы к управлению версиями баз данных.
Управление версиями баз данных: проблема
Когда вы работаете над проектом в одиночку, который еще не выпущен в production, то не возникает таких проблем, как проблема управления версиями базы данных. Вы изменяете схему данных так, как вы хотите и это всегда работает.
Проблемы возникают, когда ваша программа начинает работать в production или новый участник команды присоединяется к работе над базой данных, связанной с частью вашего проекта. Как только у вас имеется более одного экземпляра базы данных, начинается рассинхронизация. Даже с одним экземпляром, требуется значительное количество времени, чтобы синхронизировать изменения, когда более чем один разработчик работает с ней.
Выглядит знакомо? Бьюсь об заклад у вас бывали такие ситуации, более чем единожды. У меня, кончено, бывали.
Лучшие подходы к к управлению версиями баз данных
К счастью мы не одиноки. Есть множество материалов, написанных на эту тему. А также ПО, которое позволяет решать эту проблему. Я рекомендую эту книгу, если вы хотите изучить тему глубже. Это исчерпывающее руководство о том, как развивать БД совместно с кодом, который использует ее.
Хорошо, так что же это за лучшие практики для версионирования баз данных?
Best practice #1: мы должны относиться к базе данных приложения и справочным данных, как к обычному коду. Это значит, что мы должны хранить и схему, и справочные данные в системе управления версиями.
Обратите внимание, что это правило включает в себя не только схему базы данных, но также справочные данные. Справочные данные – это данные, которые обязательны для запуска приложения. Например, если у вас есть словарь всех возможных типов клиентов, на которых строится ваше приложение, вы должны хранить его в системе управления версиями.
Best practice #2: мы должны хранить все изменения в схеме базы данных и справочных данных в явном виде. Это означает, что для каждой модификации мы должны создать отдельный сценарий SQL с изменениями. Если модификация влияет как на схему, так и на справочные данные, они должны быть отражены в одном сценарии.
Следование этому правилу, является важной частью построения успешной системы управления версиями базы данных. У многих проектов хранятся схемы базы данных в системе управления версиями, но часто это просто снимок последней версии базы данных, вот и все. Все изменения в ней отслеживаются самой системой управления версиями, они не хранятся в явном виде. Кроме того, часто не отслеживаются все изменения в справочных данных.
Такие инструменты как Visual Studio подчеркивают и призывают программистов использовать автоматически сгенерированные скрипты для обновления схемы данных проекта базы данных. Это может хорошо работать в небольших проектах, но в больших проектах, отслеживание изменений в базе данных с использованием автоматически сгенерированных сценариев становится бременем. Мы будем говорить о проекте базы данных в Visual Studio и других доступных инструментах в следующем посте.
Best practice #3: каждый файл SQL скрипта должен быть неизменным после развертывания в production или промежуточную среды.
Смысл сохранения изменений в отдельных файлах состоит в том, что мы можем контролировать (отслеживать) каждый из них. Когда мы изменяем существующие сценарии SQL, мы теряем все преимущества работы с лучшими практиками версионирования баз данных предоставленных нам. Сохраняйте файлы сценариев неизменными после их развертывания. Если вам нужно откатить изменения, которые уже совершены — создайте отдельный сценарий для этого.
Best practice #4: все изменения в схеме и справочных данных в базах данных должны быть применены через скрипты. Ни один из них может быть применен вручную.
Если мы изменяем базу данных в обход наших скриптов, вся идея версионирования базы данных становится бесполезной, так что мы должны убедиться, что изменения сделаны только с помощью скриптов SQL, которые мы создаем.
Best practice #5: каждый разработчик в команде должен иметь свой экземпляр базы данных.
Часто, команды начинают с единой базы данных в среде разработки. Это хорошо работает в начале, но, когда база данных становиться достаточно большой, одновременная ее модификации становится все труднее и труднее, пока в некоторой точке не перестает работать у всех.
Часто программисты делают несовместимые изменения, так что это хорошая идея, чтобы каждый программист имел отдельный экземпляр базы данных, воизбежании таких коллизий. Если разработчики делают изменения какой-то части схемы БД одновременно, то такие конфликты могут быть разрешены с помощью системы управления версиями, как конфликты в C # / Java и т.д.
Кроме того, если у вас есть несколько веток вашей кодовой базы, вы также можете создать отдельный экземпляр базы данных для каждой из них, в зависимости от того, какие различия БД в этих ветках имеются.
Best practice #6: версии базы данных должны храниться в самой базе данных. Я, как правило, создаю отдельную таблицу с названием «Settings» и храню версию там. Не используйте сложные обозначения типа «x.y.z» для номеров версий, просто используйте целое число.
Каковы преимущества такого подхода?
Так какие же преимущества нам дают лучшие подходы к версионированию баз данных?
Первый и самый главный плюс состоит в том, что, используя этот подход, мы больше не имеем проблем с несоответствием схемы базы данных. Автоматическое обновление до последней версии решает их полностью, конечно, если мы в полной мере придерживаться правил, описанных выше.
Это особенно полезно, когда у вас нет единой production базы данных, но каждый клиент имеет свой собственный экземпляр базы данных. Управление версией БД в таких условиях может стать адом, если вы не использовать правильные техники управления версиями.
Еще одним достоинством использования лучших практик, является сильная связность изменений в базе данных. Это означает, что каждая известный модификация в схеме и справочных данных отражается в одном месте, а не разбросана по приложению.
Сценарии обновления SQL наделены сильной связностью, в том смысле, что они содержат все необходимы изменения для БД, так что легко понять, что изменения были сделаны в базе данных для того, чтобы разблокировать конкретный функционал. Сохранение схемы и изменений данных, связанных друг с другом в одном файле также сильно помогает.
Описанный в этом посте подход применим, даже если вы не следовали ему с самого начала. Чтобы использовать это на практике нужно просто создать первоначальный сценарий схемы базы данных, который вы имеет на данный момент в production и можете постепенно начать изменять его с этого момента. Текущая версия, должна стать версией #1, с которой вы можете двигаться дальше, используя подходы, которые мы обсуждали выше.
Комментарии (19)
WeslomPo
31.08.2015 11:16+6Что-то я не услышал волшебного слова «Миграция» в этом переводе. Откройте для себя новый замечательный инструмент, и вам не захочется переводить капитанские статьи. Сам перевод недурственен.
Evengard
31.08.2015 12:00Мб немного не в тему, но всё же. Кто знает как подружить EF6+npgsql+Постгрес с миграциями? (.NET)
alemiks
31.08.2015 12:43+2А что у вас не получилось? Действия те же самые, что и в случае с mssql, только в конструкторе класса Configuration нужно
SetSqlGenerator("Npgsql", new NpgsqlMigrationSqlGenerator());
alemiks
31.08.2015 12:47+1Скорее всего в статье нет слова «миграция» потому, что в названии сайта есть слово «enterprise». А это значит, что к одной БД может быть подключено десятки (а в случае запущенной стадии энтерпрайза и сотни) приложений. В каком из них делать миграции?
lair
31.08.2015 12:59+1В том, которое является основным стейкхолдером конкретного компонента БД.
Впрочем, паттерн «десятки и сотни приложений подключены к одной БД» в энтерпрайзе давно считают плохим, так что лучше стараться его избегать (я понимаю, что это мир розовых пони, но помечтать-то можно?).
Delphinum
31.08.2015 12:50+1Проблема именно в терминологии, или есть реальные отличия между инструментом «Миграции» и тем, что предлагается в статье?
WeslomPo
31.08.2015 14:05+1То что описывается в статье, на мой взгляд, и есть миграции. Это как написать статью о том как правильно вкручивать сотни шурупов не упоминая шуруповёрт.
MacIn
31.08.2015 13:45+1Versioning — управление версиями.
Best practice #6: версии базы данных должны храниться в самой базе данных. Я, как правило, создаю отдельную таблицу с названием «Settings» и храню версию там. Не используйте сложные обозначения типа «x.y.z» для номеров версий, просто используйте целое число.
У нас в mysql версия таблицы прописывается в комментарии к таблице.
kuzemchik
31.08.2015 16:08+1Посмотрите на Liquibase.
Там есть все что вы описали + много другого полезного.
savostin
31.08.2015 18:24А еще бы кто написал как сохранять предыдущую версию базы работоспособной, например, для поддержки API предыдущей версии.
MaximChistov
Какая нафиг Best Practice? Как автогенерированный write only скрипт позволит отслеживать, какие обновления уже были накачены, а какие нет? Такое надо делать кодом.
Веселее всего когда приходится накатывать апгрейды на базу от версии, отстающей от нынешней на пару цифр, там такие замечательные баги вылезают, особенно в ORM.
drakmail
Миграции в рельсах/django решают эти проблемы
relgames
«Best practice #6: версии базы данных должны храниться в самой базе данных»
Никто руками скрипты не накатывает. Для Java, к примеру, есть flyway