Миграции базы данных в сложной ветвящейся системе

В текущем рабочем процессе разработки мы ввели миграции баз данных (используя Ruckusing), чтобы сохранить схему базы данных наших разработчиков. в синхронизации. Он отлично работает, довольно прост в использовании, но теперь мы перешли на git в качестве VCS, и мы столкнулись со следующей проблемой в нашей системе управления версиями баз данных.

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

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


person ChrisR    schedule 20.06.2011    source источник
comment
Это то, что я бы назвал ящиком Пандоры. Это чрезвычайно сложно и требует множества правил и обучения для всех, кто работает над полной кодовой базой. Сюда входят правила для определения приоритета слияния ветвей, содержащих изменения БД и т. Д.   -  person NDM    schedule 06.10.2014


Ответы (5)


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

Система, с которой я сейчас работаю, использует другой подход: у нас нет возможности выполнять инкрементные миграции, а только восстанавливать базу данных по базовому уровню. Во время первоначальной разработки этой базовой линией была пустая база данных, а во время обслуживания - копия действующей базы данных (восстановленная из дампа). У нас просто есть куча сценариев SQL и XML, которые мы применяем к базовой линии, чтобы получить текущую систему (по сути, миграции, но не предназначены для инкрементного запуска). Обновить или переключить ветки очень просто: очистить базу данных, загрузить дамп, чтобы установить базовый уровень, запустить сценарии.

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

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

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

person Tom Anderson    schedule 20.06.2011
comment
Это проверенный и проверенный метод или то, что вы и ваши коллеги только что освоили? Я хотел бы еще немного почитать и изучить его, но не знаю, где искать и что искать :) - person ChrisR; 20.06.2011
comment
Это наше революционное открытие, которым я делюсь с вами по невысокой цене. На самом деле, я понятия не имею, распространен ли этот подход за пределами моей компании (на самом деле, он даже не универсален в моей компании!). Это не то, с чем я сталкивался в обсуждениях миграции и т. Д. На самом деле, это было бы хорошим вопросом для программистов. - person Tom Anderson; 20.06.2011
comment
Имейте в виду, двум людям эта идея понравилась настолько, что они проголосовали за нее, так что, возможно, они тоже используют этот подход? Мы, наверное, никогда не узнаем. - person Tom Anderson; 20.06.2011
comment
Чем отличается запуск «груды SQL- и XML-скриптов» от БД от инкрементных миграций? Вы имеете в виду, что скрипты не зависят друг от друга? По сути, это похоже на систему инкрементной миграции (начните с базовой линии, примените сценарии по порядку). - person Luke H; 26.06.2014
comment
Учитывая эту некоторую мысль, и я думаю, что если бы я использовал git, я бы не хотел принимать что-либо, из-за чего изменения ветки занимали больше нескольких секунд. Было бы интересно услышать, если бы @TomAnderson и его команда когда-нибудь обнаружили, что это проблема. - person Alex; 01.02.2017
comment
@Alex О да, использование такого подхода для смены веток заняло вечность! Первоначально достаточно долго, чтобы вы могли пойти и выпить чашку кофе, но позже в проекте ›10 минут из-за количества тестовых данных. Поэтому мы добавили уровень кэширования: после миграции инструмент сохраняет дамп на сервере общей базы данных; перед миграцией он проверяет, есть ли дамп целевого состояния, и если есть, просто загружает его. Это вернуло обычный случай к секундам. - person Tom Anderson; 04.02.2017
comment
Тем не менее, медленность миграции была связана с тем, что мы загружали много тестовых данных с помощью очень медленного инструмента ETL, который проходил через нашу ORM. Если бы тестовые данные были сценариями SQL, перестройка все равно была бы очень быстрой, и нам не понадобился бы слой кеширования. - person Tom Anderson; 04.02.2017
comment
Я так понимаю, вы не делали функциональные ветки? У нас есть несколько разработчиков, и все они работают над небольшими функциональными ветками, которые по завершении объединяются в мастер. Мы не можем позволить, ни по деньгам, ни по времени, иметь отдельную базу данных для каждой ветки. - person Tim Gautier; 22.05.2018
comment
@TimGautier Мы не делали функциональные ветки, но я не вижу, как бы все было по-другому. У нас были отдельные ветки разработки и выпуска (последняя для срочных исправлений ошибок и т. Д.), И все работало нормально. Как вы думаете, почему создание отдельной базы данных для каждого филиала потребует больше времени и денег? Я подозреваю, что здесь мы исходим из других предположений. - person Tom Anderson; 29.05.2018
comment
@TomAnderson Ну, функциональные ветки означают, что у нас много ветвей (10-20 за раз) и мы постоянно создаем новые (обычно несколько в день). Наша база данных занимает пару терабайт, и создание новой базы не является ни бесплатным, ни мгновенным. Мы бы тратили все свое время на ожидание появления БД, и цена была бы сумасшедшей. Я сделал БД для каждой ветки, и она отлично работает, но только если у вас небольшая БД или вы можете переносить очень устаревшие данные. - person Tim Gautier; 29.05.2018
comment
@TimGautier Ого, если у вас ›база данных размером 1 ТБ, тогда мой метод действительно бесполезен! Вы можете использовать аналогичный подход, имея пул подготовленных баз данных, в зависимости от того, насколько база данных различается в разных ветвях. Или наличие пула баз данных на базовом уровне, если миграции оттуда в желаемое состояние невелики. В любом случае вам может потребоваться фоновая работа по созданию свежих базовых баз данных, чтобы пул оставался заполненным. - person Tom Anderson; 30.05.2018

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

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

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

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

Мы использовали mercurial, который распространяется, как git, поэтому настройка была очень похожей. У нас также были локальные БД для разработчиков на ноутбуках разработчиков и ряд сред, как для разных клиентов, так и для разных этапов (разработка, интеграция, производство), поэтому модель была подвергнута реальным испытаниям, и она сработала на удивление хорошо. У нас были некоторые конфликты в наборах изменений, но в основном мы смогли разрешить их вскоре после появления проблемы. Локальные среды разработки были действительно самой сложной частью, поскольку во время разработки могли быть внесены некоторые изменения схемы, которые не всегда были совместимы с более поздними наборами изменений, но структурированный характер изменений и наличие известного состояния, которое нужно было вернуть, привело к очень немногим. реальные проблемы.

При таком подходе есть несколько предостережений:

  1. Все и любые изменения схемы должны быть реализованы в наборах изменений. Самая большая причина замешательства всегда заключалась в том, что кто-то просто немного возился.
  2. Первый пункт также применим, даже если вы используете инструмент, изменяющий схему, например ORM-инструмент, например Hibernate. Вам нужно хорошо разбираться в этом инструменте, чтобы понимать, какие изменения он вносит и требует.
  3. Все пользователи должны принять это и научиться следовать правилам. Проверить 1.
  4. Наступает момент, когда перенос большого количества наборов изменений начинает занимать слишком много времени. В это время вам нужно будет создать новую базовую линию, что может быть немного сложно, особенно с большим количеством ветвей. Это тоже хорошо спланировать заранее и хотя бы знать обо всех существующих ветках БД.
  5. Вам нужно немного заранее спланировать работу с ветвями, чтобы знать, собираются ли они в какой-то момент вернуться к мастеру. Наивное слияние может не работать при изменении схемы.
  6. Для очень долгоживущих ветвей и отдельных наборов данных эта модель может быть недостаточно сильной.

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

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

person Kai Inkinen    schedule 20.06.2011
comment
Вы говорите, что у вас была определенная БД / схема для данной ветки - вы имеете в виду, что у вас была какая-то справочная копия базы данных, которую вы могли скопировать в базу данных разработчика? Или у каждого разработчика была своя схема для каждой ветки? Или что-то другое? - person Tom Anderson; 30.06.2011
comment
Я не уверен, почему вы думаете, что наличие большого количества скриптов не под контролем. Если ваша команда написала и поддерживает эти сценарии, вы полностью контролируете ситуацию. Вы не доверяете какому-то стороннему инструменту правильное решение сложной проблемы, вы несете прямую ответственность за то, что делается. - person Tom Anderson; 30.06.2011

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

Я решил это, написав хук после проверки и после слияния, которые можно хорошо использовать с git. Я храню все свои миграции в виде файлов SQL в отдельном каталоге и фиксирую их вместе с измененным кодом PHP. Каждый раз, когда я выполняю

git checkout

or a

git merge

git автоматически вызовет соответствующие миграции вверх и вниз. См. Мою реализацию на Github.

В качестве специального запроса (для тех из вас, кто не хочет переходить по ссылке github) еще несколько пояснений:

Рассмотрим следующий сценарий. У вас есть две ветки:

  • master - который содержит веб-сайт, который в настоящее время находится в сети
  • особенность - которая содержит незавершенную новую функцию

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

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

    • 20151120130200-extra-field-up.sql (containing all the SQL queries to migrate upwards)
    • 20151120130200-extra-field-down.sql (продолжение миграции всех SQL-запросов вниз)
  2. When you now perform a checkout to master, the post-receive git hook will:
    1. find all *-down.sql scripts in the commits from <new HEAD>..<old HEAD>
    2. выполнить эти сценарии с локальной базой данных
    3. найти все скрипты * -up.sql в коммитах из <old HEAD>..<new HEAD>
    4. выполнить эти сценарии с локальной базой данных
  3. When you merge your feature branch into master, the post-merge hook will:
    1. find all *-up.sql scripts in the commits from master..feature
    2. выполнить эти сценарии с локальной базой данных

Установить

Просто скопируйте файл после проверки и / или после слияния в каталог .git / hooks вашего собственного репозитория git. Вы можете редактировать раздел конфигурации этих файлов. Смотрите сами файлы для объяснения.

использование

Именование файлов SQL миграции имеет решающее значение. Они должны заканчиваться на up.sql или down.sql. Остальное название полностью зависит от вас. Однако, если у вас есть одна фиксация с несколькими миграциями вверх и / или несколькими миграциями вниз, порядок, в котором они выполняются, зависит от лексикографического порядка. Файлы миграции, которые находятся в разных коммитах, всегда будут вызываться в том же (обратном) порядке, что и коммиты.

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

person Richard Brinkman    schedule 21.11.2015
comment
Вам следует добавить более подробную информацию о том, как работают эти перехватчики и как должны быть написаны сценарии миграции. (Не делайте ссылки только на код и пояснения, потому что в будущем ссылка может сломаться.) - person try-catch-finally; 21.11.2015

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

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

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

person Carl G    schedule 01.03.2013

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

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

Например, addCustomerSalt зависит от initialSchema, а separateAddress зависит от person.

Единственная проблема, которую это не решает, заключается в том, что если ветвь A зависит от обновления Z, которое было создано в ветке B, но, может быть, в этом случае вам следует выполнить переустановку на общего предка?

person Masse    schedule 15.08.2012