Привет, любители Хабра! Сегодня я решил поделиться своим вариантом бекапа данных из MySql и рассказать о том как его можно использовать для контроля версий в Git. И если вам интересно узнать, как можно контролировать состояние базы данных на всех этапах разработки, или просто делать правильные бекапчики базы своих проектиков и разворачивать из в любой момент времени, тогда прошу к прочтению!
Что это?
Это набор скриптов написанных на BASH, что позволяет им работать практически на любой машине в которой работает данная оболочка, предназначенный для облегчения создания и разворачивания бекапов. Изначальная идея была в том чтобы можно было создавать контрольные точки БД при написании проекта командой разработчиков, и хранить его в гите, я знаю что есть более серьезные вещи для такой цели, и это решение никак не претендует на их место.
Для кого?
К примеру вы предпочитаете разрабатывать сайт сразу на хостинге заказчика и контролировать ход разработки и изменения в БД. Либо у вас мало денег (или жаба душит) чтобы тратить их на хорошие продукты контроля версий БД. Вы также можете использовать проект в качестве бекапера данных по определенным правилам, который можно использовать по крону. И конечно пригодится если вы начинающий разработчик и только начинаете осваивать азы разработки, и у вас периодически происходит 500-я и вы не знаете почему. Либо вы разрабатываете продукт командой и хотите автоматически при пуше в мастер синхронизировать его с продакшеном для оценки заказчика.
Рассмотрим пример стандартной разработки сайта на стороне хостера (большинство случаев):
- Есть сервер, на котором крутится проект и скорее всего это либо локальная машина, либо хостинг клиента текущего проекта.
- Есть локальный компьютер, за которым вы работаете и по традиции там же храните файлики и снимки состояний.
- И Продакшен, это то куда сливается итоговый продукт — но он может являтся и 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.sqlA как производить импорт находясь на сервере?
  ./db_import.sh < db_backup/db.sqlВ разных проектах я использую CMS xxx и мне надоело каждый раз вводить данные
для управления БД, как можно упростить процесс?
Для этого нужно написать свой файл провайдера по аналогии с имеющимися.
Я создал задание CRON и конфигурацию с использованием провайдера php, но оно
не выполняется, либо кеш сайта CMS не очищается, в чем может быть дело?
В зависимости от настроек сервера и самого задания, задания CRON могут запускаться совсем в другом окружении, в котором путь к php препроцессору может отличаться и как следствие, запускать совсем другую версию php не совместимую с той, на которой работает ваша CMS.
Послесловие
Я никогда не писал на BASH скрипты и поэтому вероятно наговнокодил, я уверен, тут есть грамотные ребята, которые в случае заинтересованности смогут добавить свои правки. Я же буду развивать проект по мере поступающего интереса и выявлении ошибок в работе.
И не стоит сразу вонять, что ничего не работает, возможно вы не смогли разобраться в том как произвести правильную настройку и установку (в особенности если вы работаете на Windows, все же BASH это линуксовая среда).
Инструкция по установке и использованию лежит в README. Я старался сразу писать на английском, но и из-за моего любительского уровня, возможно будет не все понятно, в дальнейшем напишу на русском. Если есть желание внести правки в перевод или код, форкайте на здоровье! A если есть хорошие советы — поделитесь.
P.S: если вы дочитали до саааааамого низа, значит Вам стало интересно и не терпится попробовать :-)
Комментарии (12)
 - andreymal28.04.2019 18:35+1- У меня почему-то никогда не возникало необходимости обновлять БД при переключении веток. Если я делаю какую-нибудь фичу с изменением схемы БД, то она чаще всего быстренько вливается в мастер и дальше живётся как обычно. Если всё же приспичит менять схему и не вливать в мастер, то я просто создам отдельную временную базу и не буду париться ни с какими синхронизациями 
 - Контроль структуры таблиц осуществляется через миграции в любой ORM, как уже отметили выше. Бэкапы же данных храниться в git не должны (максимум — небольшой набор фикстур для начальной инициализации свежесозданной БД). Так что всё это выглядит довольно бессмысленно 
 - происходит 500-я и вы не знаете почему - Дык логи читать надо 
 - Для того, чтобы соблюсти контроль версии базы данных используя гит, очевидно нужно получать ее дампы - Не нужно, потому что миграции всё контролируют сами 
 - rsashka28.04.2019 18:45- При хранении дампа БД в Git есть проблема нещадного разрастания размера коммитов, если сама БД довольно большая (например, в случае рабочего сайта с CMS). 
 А если скрипт упаковать, то он будет хранится обычным бинарным файлом. - setest1 Автор29.04.2019 17:50- Есть такое, но если правильно настроить конфиг размер будет сильно уменьшен. 
 
 - apapacy28.04.2019 20:14- Согласен со всеми кто против такого подхода. За маленьким исключением. Существует достаточно специфический класс сайтов это типа визитка без контента типа новостная лента и т.п. то есть практически статика. Но при разработке юзается cms с хранением в базе данных. Да там хранить дампы в git вполне допустимо т.к. ещё раз повторю это практически статика. 
 Автор намекает что имеет дело именно с такими сайтами просто не акцентировал на этом внимание. Хотя этот момент очень важен исходя из такого нестандартного решения - rsashka28.04.2019 20:36+1- Для таких сайтов достаточно единственного скрипта без какой либо интеграции с 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 - Valsha29.04.2019 03:49- Ну понятно что возможно автор не использует какой то Фреймворк типа yii2, laravel или Django. Но просто автор уж очень сильно уходит на «тёмную сторону», он и сразу пишет что исправления все на live сервере/сайте. То есть все как то уж очень по суровому. Если уж так все сурово сразу то зачем бекапы то тогда :)  - speker29.04.2019 17:46- тут не про версионирование структуры бд, а про версионирования содержимого. Если я правильно понимаю в django для этого есть django-reversion 
 
  - setest1 Автор29.04.2019 17:45- Я прекрасно понимаю, что есть миграции, и знаю что делать когда есть 500 итд, но помимо yii2, laravel или Django итд существует 100500 CMS и самописных монстров, которые развиваются небольшими командами и постепенно перемещаются в пекло из-за гуанокодеров и развития технологий. Правильная настройка конфига позволяет сохранять только структуру без содержимого, тогда рост минимален в таком случае, а что уж с ней делать потом это второй вопрос. Я думаю что если разраб будет пользоваться снимками структуры, то он прекрасно знает как этот вопрос решить. 
 
 
 
           
 
questor
Практически в каждом ORM фреймворке есть нормальный инструмент для управления миграциями — поэтому собственная поделка на bash-скриптах имеет смысл только ну разве что для тренировки написания скриптов bash. В работе же лучше пользоваться проверенными временем решениями, которыми пользуется большинство разработчиков. Навскидку под PHP — phinx.org laravel.su/docs/5.0/eloquent
setest1 Автор
Да верно, отчасти это была тренировка в баше )