Механизм контроля версий базы данных в GIT (управление дампами MySQL)

Привет, любители Хабра! Сегодня я решил поделиться своим вариантом бекапа данных из MySql и рассказать о том как его можно использовать для контроля версий в Git. И если вам интересно узнать, как можно контролировать состояние базы данных на всех этапах разработки, или просто делать правильные бекапчики базы своих проектиков и разворачивать из в любой момент времени, тогда прошу к прочтению!


Что это?


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


Для кого?


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


Рассмотрим пример стандартной разработки сайта на стороне хостера (большинство случаев):


  1. Есть сервер, на котором крутится проект и скорее всего это либо локальная машина, либо хостинг клиента текущего проекта.
  2. Есть локальный компьютер, за которым вы работаете и по традиции там же храните файлики и снимки состояний.
  3. И Продакшен, это то куда сливается итоговый продукт — но он может являтся и 1-м пунктом просто другой папкой.

Как с этим работать?


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


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


У каждого скрипта можно получить справку классическим путем через аргумент -h либо --help.


Я не рекомендую бекапить всю БД, гит не любит больших файлов, да и в большинстве случаев это и не нужно. Поэтому вы можете легко настроить используя config.ini Так как настройки используются и на серверной стороне (на которой поднят mySql) и клиентской (комп разработчика), то за настройку отвечает один и тот же файл. И Конечно это может быть один и тот же компьютер, если вы ведете разработку локально.


Что можно настраивать для создания дампа


  • Настройки подключения к БД и пути храния дампов
  • Указание провайдера, который сможет не только получить данные подключения к БД, но
    и правильно очистить кеш на сервере при переключении веток.
  • Экспорт всей БД
  • Список таблиц которые нужно исключить из экспорта
  • Либо экспорт определенных таблиц
  • Сохранение структуры без вставки данных у определенных таблиц
  • Указание полей у таблиц значения которых не должны экспортироваться, и должны
    быть заменены значениями по умолчанию.
  • Отдельные наборы правил в одном конфиге, что позволит делать различные бекапы по CRON-у

Для того чтобы облегчить процесс создания дампов. Я использовал файлы — провайдеры. И настроил (пока один единственный) для CMS MODX revolution. На его основе можно написать такой же провайдер для любой CMS.


Краткие шаги к началу работы


Скачаем проект, рекомендую положить в папку рядом с .git, либо внутрь проекта, скрипт все равно найдет git

[git clone ](https://github.com/Setest/.git-db-watcher)

установим права на запуск на локальном компе и сервере
chmod +x install.sh;

ставим локально
./install.sh

ставим на сервере, где не нужны хуки

./install.sh -nh

Теперь необходимо внести правки в config.ini. Для примера вот так:


;общий раздел
[hooks]
  ; H_CHECK_DB_HASH_BEFORE_CHECKOUT=1
  ; импорт файла БД даже если checkout происходит на эту же ветку
  ; также это правило распростроняется при переключении на вновь созданную ветку
  ; через git checkout -b new_branch_name
  H_CHECKOUT_FORCE=0
  ; автоматическое добавление файла БД при каждом комите
  H_CHECKOUT_EVERCOM=1
  ;автоматическая очистка директории кеша после переключения БД
  H_CHECKOUT_CLEARCACHE=1

[common]
  ;результирующий файл в который будут экспортироваться данные
  EXPORT_FILE="db.sql"
;на локальном компе используется из хуков гита и ручного экспорта БД
;через запуск файла ./export.sh

[develop]
  ;скрипт запуска db_export.sh на сервере
  CLI_DB_EXPORT="ssh host '/path/to/project/on/server/.git-db-watcher/db_export.sh'"
  CLI_DB_IMPORT="ssh host '/path/to/project/on/server/.git-db-watcher/db_import.sh'"

;только на сервере
[server]
  PHP_PATH="/usr/local/bin/php"
  CONFIG_INC_PATH="/path/to/project/on/server/core/config/config.inc.php"
  PROVIDER=modx
  DB_TABLES_INCLUDE=site_content
  DB_TABLES_AUTOPREFIX=1

[server_full_site]
  PHP_PATH="/usr/local/bin/php"
  CONFIG_INC_PATH="/path/to/project/on/server/core/config/config.inc.php"
  ; '' - берет данные из настроек с префиксом DB_CONFIG_ иначе из файла лежащего
  ; папке providers
  PROVIDER=modx
  ;не обязательно но можно указать настройки вручную если нет провайдера
  DB_CONFIG_HOST=
  DB_CONFIG_TYPE=
  DB_CONFIG_USER=
  DB_CONFIG_PASSWORD=
  DB_CONFIG_CONNECTION_CHARSET=
  DB_CONFIG_DBASE=
  DB_CONFIG_TABLE_PREFIX=
  DB_CONFIG_DATABASE_DSN=
  ;если указаны то будут экспортированы только эти таблицы (разделитель пробел)
  ;заворачивать строку в кавычки нельзя
  ; DB_TABLES_INCLUDE=manager_log register_messages user_attributes
  ; DB_TABLES_INCLUDE=site_content
  ;таблицы исключаемые из экспорта
  ; DB_TABLES_EXCLUDE=session register_messages mse2_words ec_messages
  ;добавление префиксов, взятых из файла конфигурации, к именам таблиц
  DB_TABLES_AUTOPREFIX=1
  ;таблицы из которых будет удалены запросы на INSERT
  DB_TABLES_REMOVE_INSERT="manager_log session register_messages"
  ; DB_TABLES_REMOVE_INSERT="manager_log"

  ;список таблиц поля которых будут выставлены по умолчанию
  ; DB_TABLES_DEFAULT=user_attributes users
  DB_TABLES_DEFAULT=user_attributes
  ;список полей соответствующих таблице, значения кот будут выставлены
  ;по умолчанию в соответствие с со структурой таблицы, это не обязательно и
  ; можно не указывать
  DB_TABLES_DEFAULT_user_attributes=sessionid logincount lastlogin thislogin
  ; DB_TABLES_DEFAULT_users=session_stale

;получает только пользователей, с удалением лишних данных
[only_users]
  DB_TABLES_INCLUDE=user user_attributes
  EXPORT_FILE="users.sql"
  DB_TABLES_DEFAULT=user_attributes user
  DB_TABLES_DEFAULT_user_attributes=sessionid logincount lastlogin thislogin
  DB_TABLES_DEFAULT_users=session_stale

Если все настроено правильно можно запустить ./export.sh и у вас должен
появиться дамп БД на локальном компе и на сервере.


Остальные вопросы


Мне нужно сохранить результат на сервере в другое место:

  ./db_export.sh --output 1>./xxx.sql

Хочу производить экспорт на сервере используя данные своего раздела файла конфигурации:

  ./db_export.sh -с=only_users --output 1>./users.sql

Хочу импортировать файл БД, но не хочу это делать через перехватчики GIT-а?

  ./import.sh
  ./import.sh EXPORT_FILE=site_name.sql
  ./import.sh DB_BACKUP_FILE=/.../../site_name.sql
  ./import.sh --config=site DB_BACKUP_FILE=./site_name.sql

A как производить импорт находясь на сервере?

  ./db_import.sh < db_backup/db.sql

В разных проектах я использую CMS xxx и мне надоело каждый раз вводить данные
для управления БД, как можно упростить процесс?

Для этого нужно написать свой файл провайдера по аналогии с имеющимися.


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

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


Послесловие


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


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


Инструкция по установке и использованию лежит в README. Я старался сразу писать на английском, но и из-за моего любительского уровня, возможно будет не все понятно, в дальнейшем напишу на русском. Если есть желание внести правки в перевод или код, форкайте на здоровье! A если есть хорошие советы — поделитесь.


P.S: если вы дочитали до саааааамого низа, значит Вам стало интересно и не терпится попробовать :-)


Тогда тычь смелее на эту ссылочку!

Комментарии 13

    0
    Практически в каждом ORM фреймворке есть нормальный инструмент для управления миграциями — поэтому собственная поделка на bash-скриптах имеет смысл только ну разве что для тренировки написания скриптов bash. В работе же лучше пользоваться проверенными временем решениями, которыми пользуется большинство разработчиков. Навскидку под PHP — phinx.org laravel.su/docs/5.0/eloquent
      0
      Да верно, отчасти это была тренировка в баше )
        0
        Если речь идет о разработке, к примеру, веб приложения, то да — миграции наше все. Как насчет приложений, которые базируются исключительно на базе данных. К примеру, разнообразные бизнес аналитические тулзы. По факту, там имеется одна, а то и несколько баз данных с кучей таблиц, триггеров и хранимых процедур. Данные туда приходят из разных источников. Отчеты генерятся специальной тулзой. И тут встает вполне резонный вопрос. Как сделать, что бы структура базы с триггерами и процедурами, лежала в гите, и было бы удобно редактировать, смотреть изменения, откатывать изменения админу баз данных.
        +1

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


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


        происходит 500-я и вы не знаете почему

        Дык логи читать надо


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

        Не нужно, потому что миграции всё контролируют сами

          0
          При хранении дампа БД в Git есть проблема нещадного разрастания размера коммитов, если сама БД довольно большая (например, в случае рабочего сайта с CMS).
          А если скрипт упаковать, то он будет хранится обычным бинарным файлом.
            0
            Есть такое, но если правильно настроить конфиг размер будет сильно уменьшен.
            0
            Миграции? Нет, не слышали :)
              0
              Согласен со всеми кто против такого подхода. За маленьким исключением. Существует достаточно специфический класс сайтов это типа визитка без контента типа новостная лента и т.п. то есть практически статика. Но при разработке юзается cms с хранением в базе данных. Да там хранить дампы в git вполне допустимо т.к. ещё раз повторю это практически статика.
              Автор намекает что имеет дело именно с такими сайтами просто не акцентировал на этом внимание. Хотя этот момент очень важен исходя из такого нестандартного решения
                0
                Для таких сайтов достаточно единственного скрипта без какой либо интеграции с git. Это проще и значительно понятнее.
                Например для Drupal 7
                #!/bin/bash
                
                # one argument: the file of config.php file
                d7config=$1
                
                if [ ! -f "$d7config" ]; then
                echo "File '$d7config' not found."
                exit
                fi
                
                db=`cat $d7config | grep "      'database' => '" | awk -F"'" '{print $4}'`
                if [ -z $db ]; then
                echo "Database name not found in $d7config."
                exit
                fi
                
                user=`cat $d7config | grep "      'username' => '" | awk -F"'" '{print $4}'`
                if [ -z $user ]; then
                echo "Database user not found in $d7config."
                exit
                fi
                
                pw=`cat $d7config | grep "      'password' => '" | awk -F"'" '{print $4}'`
                if [ -z $pw ]; then
                echo "Database credentials not found in $d7config."
                exit
                fi
                
                #pr=`cat $d7config | grep "      'prefix' => '" | awk -F"'" '{print $4}'`
                
                
                tables_cmd=""
                ignore_cmd=""
                
                for t in "%cache%" "%history" "%search_%" "%sessions" "%watchdog" "%accesslog"
                do
                  for t2 in `mysql $db --user=$user --password=$pw -Bse "show tables like \"$t\";"`
                  do
                    tables_cmd="$tables_cmd $t2"
                    ignore_cmd="$ignore_cmd --ignore-table=$db.$t2"
                  done
                done
                
                
                # Дамп данных без временных данных и кеша, одна строка данных в строке, без даты создания дампа, без создания БД. Добавление и удаление таблиц и все в одну транзакцию
                mysqldump --user=$user \
                    --password=$pw \
                    --extended-insert=FALSE \
                    --skip-dump-date \
                    --no-create-db \
                    --add-drop-table \
                    --single-transaction \
                    $ignore_cmd \
                    $db \
                    > mysqldump.sql
                
                
                # Дамп только структуры таблиц без данных, без даты создания дампа, без создания БД + добавить удаление таблиц
                mysqldump --user=$user \
                    --password=$pw \
                    --databases $db \
                    --skip-dump-date \
                    --no-create-db \
                    --add-drop-table \
                    --no-data \
                    --tables $tables_cmd \
                    >> mysqldump.sql
                
                

                0
                Ну понятно что возможно автор не использует какой то Фреймворк типа yii2, laravel или Django. Но просто автор уж очень сильно уходит на «тёмную сторону», он и сразу пишет что исправления все на live сервере/сайте. То есть все как то уж очень по суровому. Если уж так все сурово сразу то зачем бекапы то тогда :)
                  0
                  Это не «тёмная сторона», там совсем нет печенек, только сухари
                    0
                    тут не про версионирование структуры бд, а про версионирования содержимого. Если я правильно понимаю в django для этого есть django-reversion
                    0
                    Я прекрасно понимаю, что есть миграции, и знаю что делать когда есть 500 итд, но помимо yii2, laravel или Django итд существует 100500 CMS и самописных монстров, которые развиваются небольшими командами и постепенно перемещаются в пекло из-за гуанокодеров и развития технологий. Правильная настройка конфига позволяет сохранять только структуру без содержимого, тогда рост минимален в таком случае, а что уж с ней делать потом это второй вопрос. Я думаю что если разраб будет пользоваться снимками структуры, то он прекрасно знает как этот вопрос решить.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое