Comments 55
Можно например взглянуть на liquibase, или на flyway (это лишь два первых пришедших в голову, и далеко не единственные), где подобные (и еще кое-какие другие полезные) концепции давно реализованы. Идее хранить список примененных патчей в самой базе — наверное сто лет в обед.
И кстати, вынужден вас огорчить — на ваши же три вопроса:
«Кто создал столбец?»
«Что здесь должно храниться? Откуда эти числа? Пишите хотя бы комментарии!»
«Мы это уже 100 лет как не используем. Откуда оно здесь?»
данный велосипед ответов все равно не дает. Во всяком случае — простых и полных ответов.
> «Что здесь должно храниться? Откуда эти числа? Пишите хотя бы комментарии!»
> «Мы это уже 100 лет как не используем. Откуда оно здесь?»
Семь бед — один git blame.
Если по commit message непонятно, зачем и откуда появилась миграция БД — это должно было быть выявлено на этапе code review.
Blame на этот вопрос не отвечает, точнее отвечает не на этот. Blame это второй этап — когда мы знаем скрипт, мы можем узнать автора. А первый этап тут как раз не раскрыт.
Вопрос же «что тут хранится» вообще к blame никаким боком по-моему не относится.
И в общем случае эта задача полноценно не решается ни одним из известных мне фреймворков, по крайней мере теми из них, где можно писать миграции самостоятельно (и следовательно, применять в их коде произвольный SQL), что не позволяет фреймворку в принципе проанализировать, что же на самом деле миграция делает.
В идеале, конечно, нужно в комментарии к таблице/колонке ставить комментарий с ID миграции — но не уверен, что все СУБД это поддерживают и не уверен, что есть системы управления миграциями, которые это умеют.
— 001_create_users_table.sql
— 002_create_posts_table.sql
— 003_add_author_to_posts.sql
Есть засада — при работе в параллельных ветках могут появиться две миграции с одним id — но это элементарно проверяется во время CI
И кстати, выявить дубли id легко — а вот исправить может быть не так-то просто.
Что до конвенций… ну вот вам типичная миграция в моем проекте:
— найти constraint (некоторые из них были созданы давно, и соглашениям об именовании не соответствуют), как правило это PK или FK, и имя сгенерировано автоматически.
— drop constraint по найденному ранее имени
— drop computed column
— alter table alter column
— re-create constraint
— re-create computed column
Это все одна достаточно типовая сделанная вручную миграция. Всего-лишь изменение типа колонки, на которую завязаны либо constraint, либо другие вычисляемые колонки. Почему так? А потому что MS SQL, например, иначе не умеет, даже в enterprise edition. Если у вас база большая, то все может быть еще намного более грустно.
Наименование вида add_author_to_posts ничего нам в данном случае не даст, потому что оно отражает бизнес смысл миграции, а какие конкретно объекты базы она затронула — тут не говорится.
Т.е. по хорошему, механизм миграций должен бы уметь как минимум группировать операции DDL, которые умеет текущая база, и понимать, что вот эти вот пять или десять изменений — это часть одного большого изменения с точки зрения бизнеса. И еще понимать, какие конкретно объекты примененный конкретный скрипт затронул, и что он с ними сделал. А это совсем не просто, и один очевидный случай уже озвучивали — отличить переименование колонки от drop + create это задачка вовсе не для grep, тут даже анализ SQL не всегда может помочь.
Любой тул для миграций хранит в БД идентификатор последней примененной миграции. Если же тул умеет применять миграции в разном порядке (alembic) — он же и умеет выводить текущее состояние базы.
> Т.е. по хорошему, механизм миграций должен бы уметь как минимум группировать операции DDL, которые умеет текущая база, и понимать, что вот эти вот пять или десять изменений — это часть одного большого изменения с точки зрения бизнеса. И еще понимать, какие конкретно объекты примененный конкретный скрипт затронул, и что он с ними сделал. А это совсем не просто, и один очевидный случай уже озвучивали — отличить переименование колонки от drop + create это задачка вовсе не для grep, тут даже анализ SQL не всегда может помочь.
Поэтому для этого есть программист, который умеет парсить SQL и комментарии.
Ну так о том и речь. К сожалению, с пониманием даже синтаксиса SQL для более чем одной базы у многих инструментов большие проблемы. С пониманием метаданных из базы впрочем тоже. Т.е. например, вполне себе коммерческий SQLDBX не понимает некоторые специфичные для для MS SQL вещи.
Вообще в идеале я бы предпочел наверное иметь инструмент, который хранил бы модель в виде скажем ER, сам бы следил за ее версионированием, умел показывать структурные различия между версиями модели, а скрипты для модификации базы генерировал бы сам, с учетом того, что это за база (т.е. Oracle или скажем MS SQL), и какая версия модели данных там на сегодня установлена.
И чтобы инструмент сам бы понимал, какие из модификаций внесенных в ER-модель, в какие скрипты выливаются, и помнил бы историю для конкретной базы, и общую историю модели.
Но я таких идеальных инструментов не видел. Для тех что видел — как правило типичны проблемы с версионированием. Возможно они есть и стоят больших денег, поэтому никто из работодателей их не покупает.
make migration
и получаем в папке migrations код обновления схемы до новой версии.
В общем случае полностью автоматическое создание такого кода невозможно (без логики не понять, удалили один столбец и создали другой или просто переименовали столбец), но как минимум у Django есть django-admin makemigrations
В VS есть тип проекта для баз данных и набор инструментов для миграций. Вместе с контролем версий, основную работу можно сделать мышкой.
Естественно, он может отловить не все, например, переименования таблиц и индексов надо дописывать вручную. Но 80% простых миграций генерируются автоматически, но всегда нужно проверять, какие миграции получаются на выходе.
Первая проблема тут в том, что создание миграций должно быть stateful, а не stateless. Миграция зависит не от текущих исходников — а от разности текущих исходников и прошлой версии. Это выбивается из идеологии make
. Создание миграции больше похоже на git commit, чем на make.
Вторая проблема — в том, что автоматически можно делать только простейшие миграции. Любое крупное изменение структуры БД обычно требует ручной донастройки.
creates output with DDL statements that can be used to update old database schema to new one
даже на уровне только схемы часто возникают коллизии. как минимум большинство подобных инструментов (за данный конкретный не скажу) работают исключительно на добавление.
а есть еще данные, которые нужно как-то мержить между площадками.
— ALTER TABLE… RENAME TO
— ALTER TABLE… RENAME COLUMN… TO…
diff сгенерирует максимум:
— DROP COLUMN / ADD COLUMN
попробовал сейчас с Datagrip выполнить сравнение (хоть инструмент и сыроват местами, но чего ждать от недавно вышедшего продукта).
вместо вьюшки мне было предложено создать таблицу. не знаю, изобретался ли там велосипед — проблема может быть общей.
А я все миграции пишу сразу патчами, и выводить патчи из разницы состояний не приходится. Но у меня строго последовательное версионирование. Впрочем, до меня, до введения версионирования, были в патчах сентенции типа "если нет столбца добавить столбец" и т.п. и просто было две обновляемых очереди патчей — ежедневная, относительно свежая, при запуске приложения, и по запросу (практически — повторение всех патчей, каждый при этом проверяют необходимость применения самих себя). И всё работало и не ломалось.
В самом простом исполнении это обычные INSERT-запросы со светлой надеждой на стабильность и предсказуемость автоинкремента.
Зачем таким таблицам автоинкремент если надо одинаковый ID во всех копиях БД? Если это таблицы с метаданными, которые могут меняться только очередным скриптом обновления, то тут просто надо убрать автоинкремент и явно задавать значение ID в INSERT-запросе.
mysql --user ${username} --password=${password} -D${database} < ${f} # Explosion mysql --user ${username} --password=${password} -D${database}<<<"INSERT INTO dc (code, type, result) values ('${f}', 'LOAD TRIGGER', "$?")" # OK if [ $? -ne 0 ]; then exit $? fi
Более того, что произойдет, если миграция применилась частично? Что произойдет, если скрипт по какой бы то ни было причине упадет в середине применения миграции (ребут сервера, потеря сети, что угодно)? Что произойдет, когда необходимо произвести даунгрейд базы?
Пожалуйста, прекратите изобретать велосипед и поищите инструмент для миграций на вашем ЯП, их миллион самых разных. Если по какой-либо причине инструмента для миграций не сделали — есть много разных утилит, которые можно использовать независимо от ЯП проекта.
При этом:
— Все миграции обернуты в транзакции, следовательно миграции атомарны. К сожалению, в mysql нет транзакций для изменения схем данных, но это можно обойти. Миграции данных будут атомарны везде.
— У этих тулов есть community — вы перестаете быть незаменимым (и можете спокойно ходить в отпуск), плюс вы избавитесь от overhead на поддержание своей утилиты для миграций.
— Вы получите не только тот функционал, который есть сейчас, но и как минимум получите возможность rollback. В случае с mattes/migrate можно даже переиспользовать те SQL-скрипты, которые есть у вас.
за ссылки огромнейшее спасибо — ознакомлюсь то ли это.
атомарность миграции вещь полезная, но не обязательная. предложенный принцип работы таков, что все отрабатывает как с бусинками. нанизываются команды одна за другой. если встретим проблему — работа прекратится и продолжится со скрипта, который не прошел. хотя в случае обрыва связи вопрос. надо проверить опытом.
даунгрейд в текущей реализации не предусмотрен. хотя для себя я всегда держу rollback скрипты к каждому изменению (где это возможно, разумеется).
При создании связей в записях ни в коем случае не используются значения идентификаторов. (кроме полученных в вычислениях)
Что имеется в виду и почему?
Почитайте про миграции и репликацию, первое поможет с отслеживание изменений, второе с расхождением состояния баз на серверах если они у вас одни и те же.
Использование подобных миграций никак не исключает следованию принципов неразрушающих изменений (не удаляем столбцы, не переименовываем, не меняем тип данных и т.п.). Даже если приложение односерверное, при деплое такого кода мы получим момент, когда код ожидает поле, а в базе его уже (или еще) нет, что черевато фатальными ошибками. И эта проблема может становится еще более актуальной, когда у нас много нод приложений с одной базой.
Это можно было бы попробовать решить разделяя миграции на две части — созидательную (добавление), исполняемую до деплоя кода, и разрушительную (удаление), исполняемую после деплоя кода. Но мантейнить такое сложновато, да и все-равно придется учитывать неразрушающие правила для разделения таких миграций. Гораздо проще просто помечать ненужные колонки как deprecated и переодически их чистить.
Именно на несколько миграций позже это и откладывается. Главное, что бы разработчики не забывали помечать такие элементы схемы как deprecated. Или же были подготовлены сответствующие скрипты синхронизации со схемой приложения (например, в случае использования ORM). Такие изменения прекрасно делаются и без остановки сервиса, равно как и бакапы.
Как я базу в GIT закачивал