- PVSM.RU - https://www.pvsm.ru -
Здравствуйте, меня зовут Евгений, и я веб разработчик. Несколько лет назад мне перепала функция DBA (Database Administrator), я получил по этому поводу несколько сертификатов и решал соответствующие задачи. Я давно хотел описать задачу версионирования базы данных, но мне казалось, что для этого должны быть какие-то беспроигрышные варианты, которые хорошо знают умелые дяди, а я просто чего-то недопонимаю. Вчерашнее собеседование и последующий поиск по тематическим ресурсам показал, что это не так, и задача действительно сложна, актуальна и не решается однозначно. Разберём её по пунктам.
Мы используем контроль версий только для DDL (Data Definition Language) запросов. Сами данные нас не интересуют. Почему? Рассмотрим два крайних случая.
Если вы работаете со сложной базой данных, то таблицы, как ни странно, в ней наименее интересны (хотя тоже должны быть под контролем версий). Гораздо сложнее приходится с бизнес логикой, которая содержится в триггерах, представлениях, пакетах и процедурах, и тому подобных объектах. К примеру, в одной из баз, с которыми я работал, были пакеты размером до полутора мегабайт. В эти пакеты постоянно вносятся правки, и жизненно необходимо знать, кто внёс правки, когда, желательно знать, зачем, и как бы нам это откатить до любого нужного состояния.
Представим себе идеальный мир, в котором у нас есть чёткое ТЗ, которое не меняется до завершения проекта. Релиз, после которого мы забываем о том, что делали, и получаем регулярную зарплату за красивые глаза. Идеальный код, который мы писали сразу с учётом всех нюансов, который работает без ошибок и не требует сопровождения. Отсутствие доработок, срочных багфиксов, механизмов интеграции, возможность работать на тестовой базе и тестовых выборках, наличие повсеместных юнит тестов, которые говорят, что всё безупречно.
В этом случае, нам более чем достаточно использовать систему контроля версий в качестве первичного источника информации о состоянии БД, и выкатывать из неё изменения на базу. Есть единый репозиторий, есть совместная работа над кодом базы — всё красиво и прозрачно. Есть несколько продуктов, которые вроде как неплохо реализуют данный функционал.
Теперь открываем глаза и смотрим вокруг. В большинстве случаев, проект реализуется по схеме, которую я бы назвал СВВ (сделал, выкатил, выкинул). Огромный процент завершённых проектов не может продаться и закрывается без всяких перспектив на будущее. Оставшиеся счастливчики переживают сотни итераций, после которых от изначального ТЗ остаётся в лучшем случае название. В этой реальности нас в первую очередь волнует не скорость работы продукта, его требования и качество. Нас волнует скорость разработки, поскольку, кроме очевидных причин, от неё зависит самая большая часть бюджета проекта — стоимость работы в часах разработки.
Да, это неправильно. Мир жесток, несправедлив, динамичен, и требует мгновенной реакции, даже если при этом страдает качество. Все разработчики стремятся в душе к идеальному коду, но большинство принимает условия сделки с дьяволом, и ищет допустимый компромисс качества и скорости. Мы стараемся делать, как лучше, но учимся не краснеть, если вместо срока в полгода и идеального продукта мы за две недели сделали нестабильное и местами некрасивое решение. Более того, в какой-то момент приходит понимание, что “последний баг” никогда не будет найден, и всё, что мы можем — просто в какой-то момент прекратить его искать и сделать релиз. Доводить решение до идеала — удел простейших приложений и консольных скриптов — да и то часто не удаётся учесть какие-то нетривиальные моменты. Когда же мы говорим о крупных проектах, то пример Oracle, Microsoft и Apple нам показывает, что идеального кода не бывает. Как пример — классический ответ DBA на вопрос о том, что в новом релизе Oracle Database — “убрали 30% старых багов, добавили 40% новых”.
Во что же это выливается, если мы говорим о БД? Обычно это так:
Далее, если к DBA приходит разработчик и просит вернуть предыдущую версию его объекта, то DBA может это сделать в трёх случаях (на примере Oracle):
Самый реальный вариант — третий. Но он осложняется тем, что зачастую неизвестно, на какую дату нужно выполнить восстановление, и восстановление базы размером, скажем, в 10 террабайт — это довольно долгая и ресурсоёмкая операция. Так что обычно DBA просто разводит руками, разработчик хмуро выпивает кофе и идёт писать своё объект с нуля.
Что мы можем сделать, чтобы упростить жизнь разработчикам? Я вижу единственный вариант — версионировать базу по факту уже совершённых изменений. Естественно, это не даёт никакой возможности предупредить возможные ошибки — зато даст способ в большом проценте случаев вернуть к жизни нужный объект и всю систему.
Первое простое решение “в лоб” — это просто периодически выгружать всю базу. Но выгрузка базы занимает длительное время, и тогда мы не будем знать, кто, когда и что менял. Так что явно требуется что-то сложнее. А именно — нам нужно выгружать только изменённые DDL объекты. Для этого можно использовать два подхода — использовать Audit, или создать системный триггер. Я воспользовался вторым способом. Тогда последовательность получается такая:
При этом для каждого действия мы можем получить довольно подробную информацию, включая полный текст запроса, схему, имя и тип объекта, IP адрес пользователя, сетевое имя его машины, имя пользователя, тип и дату изменений. Как правило, этого хватает, чтобы потом найти разработчика и выдать медаль.
Далее нам хочется иметь репозиторий, в котором в интуитивном виде будет представлена структура базы, чтобы иметь возможность сравнивать различные версии объекта. Для этого, при каждом изменении базы требуется выгрузить изменённые объекты и закоммитить в базу. Ничего сложного! Создаём Git репозиторий, сначала делаем туда полную выгрузку, затем создаём сервис, который мониторит нашу таблицу изменений, и в случае появления новых записей выгружает изменившиеся объекты.
Side by side сравнение [1]
Список объектов [2] в схеме
История изменений [3] конкретного объекта
То же самое на github [4]
То есть, у нас есть рабочий инструмент, при помощи которого можно найти источник любых изменений в базе и при необходимости их откатить. В моём случае, наиболее крупный Git репозиторий в Gitlab (свой инстанс на отдельной машине) занимает несколько сотен мегабайт, в нём около сотни тысяч коммитов, и при этом он достаточно шустро работает. До переезда на Gitlab этот же репозиторий отлично жил на github, а потом на bitbucket.
Данные о каких объектах мы после этого имеем:
Так же можно модифицировать программу под задачу обновления устаревшей базы данных — выгружаем старую версию, поверх неё выгружаем новую, исправляем разницу в полуавтоматическом режиме.
В качестве рекомендации так же добавлю, что лучше периодически выгружать текущую версию поверх того, что есть в репозитории — на случай каких-то изменений, которые не смогла покрыть логика алгоритма выгрузки.
Ссылка на мой алгоритм для PHP и руководство по установке есть в конце статьи, но я искренне рекомендую вам пользоваться им только для справки — он был написан давно и очень криво левой рукой во время выполнения других задач. Единственный плюс — в том, что, как ни странно, он работает.
Искренне желаю вам, чтобы вам не пришлось работать с таким workflow. И надеюсь, что эта публикация вам поможет, если ваш мир всё же далёк от идеального.
А вы версионируете свою базу данных? Правильным образом, или по факту? Может быть, есть реализации под другие СУБД — MySQL, Postgres? Или есть какой-то фундаментально другой хороший подход, который я проглядел?
Автор: jehy
Источник [11]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/razrabotka/112446
Ссылки в тексте:
[1] Side by side сравнение: https://habrastorage.org/files/1e0/ae6/37c/1e0ae637cb064ffca9434f4624219018.png
[2] Список объектов: https://habrastorage.org/files/ea7/529/cb3/ea7529cb3f0449b49de44bf9eaa946ef.png
[3] История изменений: https://habrastorage.org/files/e41/578/17f/e4157817f19045aca265f07cfa516d7f.png
[4] на github: https://habrastorage.org/files/429/a19/cbf/429a19cbf1334b4f87f0c9fd1c25005a.png
[5] stackoverflow: http://dba.stackexchange.com/questions/283/how-do-you-version-your-oracle-database-changes
[6] Liquibase: http://www.liquibase.org/
[7] старая реализация: https://github.com/qwazer/oracle-ddl2svn
[8] Веб сайт моего инструмента: http://oracletogit.ru/
[9] Репозиторий : https://github.com/jehy/OracleToGit
[10] тут: http://whois.jehy.ru
[11] Источник: https://habrahabr.ru/post/277359/
Нажмите здесь для печати.