Привет, любители Хабра! Сегодня я решил поделиться своим вариантом бекапа данных из 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: если вы дочитали до саааааамого низа, значит Вам стало интересно и не терпится попробовать :-)


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

Комментарии (12)


  1. questor
    28.04.2019 17:33

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


    1. setest1 Автор
      29.04.2019 17:51

      Да верно, отчасти это была тренировка в баше )


  1. andreymal
    28.04.2019 18:35
    +1

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


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


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

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


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

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


  1. rsashka
    28.04.2019 18:45

    При хранении дампа БД в Git есть проблема нещадного разрастания размера коммитов, если сама БД довольно большая (например, в случае рабочего сайта с CMS).
    А если скрипт упаковать, то он будет хранится обычным бинарным файлом.


    1. setest1 Автор
      29.04.2019 17:50

      Есть такое, но если правильно настроить конфиг размер будет сильно уменьшен.


  1. Valsha
    28.04.2019 19:52

    Миграции? Нет, не слышали :)


  1. apapacy
    28.04.2019 20:14

    Согласен со всеми кто против такого подхода. За маленьким исключением. Существует достаточно специфический класс сайтов это типа визитка без контента типа новостная лента и т.п. то есть практически статика. Но при разработке юзается cms с хранением в базе данных. Да там хранить дампы в git вполне допустимо т.к. ещё раз повторю это практически статика.
    Автор намекает что имеет дело именно с такими сайтами просто не акцентировал на этом внимание. Хотя этот момент очень важен исходя из такого нестандартного решения


    1. rsashka
      28.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
      
      


  1. Valsha
    29.04.2019 03:49

    Ну понятно что возможно автор не использует какой то Фреймворк типа yii2, laravel или Django. Но просто автор уж очень сильно уходит на «тёмную сторону», он и сразу пишет что исправления все на live сервере/сайте. То есть все как то уж очень по суровому. Если уж так все сурово сразу то зачем бекапы то тогда :)


    1. n0wheremany
      29.04.2019 10:38

      Это не «тёмная сторона», там совсем нет печенек, только сухари


    1. speker
      29.04.2019 17:46

      тут не про версионирование структуры бд, а про версионирования содержимого. Если я правильно понимаю в django для этого есть django-reversion


  1. setest1 Автор
    29.04.2019 17:45

    Я прекрасно понимаю, что есть миграции, и знаю что делать когда есть 500 итд, но помимо yii2, laravel или Django итд существует 100500 CMS и самописных монстров, которые развиваются небольшими командами и постепенно перемещаются в пекло из-за гуанокодеров и развития технологий. Правильная настройка конфига позволяет сохранять только структуру без содержимого, тогда рост минимален в таком случае, а что уж с ней делать потом это второй вопрос. Я думаю что если разраб будет пользоваться снимками структуры, то он прекрасно знает как этот вопрос решить.